我想為可呼叫物件撰寫裝飾器函式。
這就是我現在所擁有的:
#include <utility>
template <typename DecoratedT, typename CallableT>
constexpr auto before_callable(DecoratedT &&decorated, CallableT &&callBefore)
{
return [decorated = std::forward<DecoratedT>(decorated),
callBefore = std::forward<CallableT>(callBefore)](auto &&...args){
callBefore(std::as_const(args)...); // TODO: ignore parameters?
auto &&res = decorated(std::forward<decltype(args)>(args)...);
return res;
};
}
template <typename DecoratedT, typename CallableT>
constexpr auto after_callable(DecoratedT &&decorated, CallableT &&callAfter)
{
return [decorated = std::forward<DecoratedT>(decorated),
callAfter = std::forward<CallableT>(callAfter)](auto &&...args){
auto &&res = decorated(std::forward<decltype(args)>(args)...);
callAfter(std::as_const(args)...); // TODO: ignore parameters?
return res;
};
}
template <typename DecoratedT, typename CallBeforeT, typename CallAfterT>
constexpr auto decorate_callable(DecoratedT &&decorated,
CallBeforeT &&callBefore,
CallAfterT &&callAfter)
{
return before_callable(after_callable(std::forward<DecoratedT>(decorated),
std::forward<CallAfterT>(callAfter)),
std::forward<CallBeforeT>(callBefore));
}
decorated當物件的回傳型別不是時,此代碼有效void。否則出錯:
<source>:21:24: error: forming reference to void
21 | auto &&res = decorated(std::forward<decltype(args)>(args)...);
| ^~~
#include <iostream>
template <typename SumT>
void print(const SumT& sum)
{
const auto &res = sum(4, 811);
std::cout << res << std::endl;
}
int main()
{
struct {
int operator()(int lhs, int rhs) const
{
std::cout << "summing\n";
return lhs rhs;
}
} sum{};
const auto &printBefore = [](const int lhs, const int rhs, const auto &...){
std::cout << "Before sum (args): " << lhs << " " << rhs << std::endl;
};
const auto &printAfter = [](const auto &...){
std::cout << "After sum" << std::endl;
};
std::cout << "Undecorated: ";
print(sum);
std::cout << "\nDecorated Before:\n";
print(before_callable(sum, printBefore));
std::cout << "\nDecorated After:\n";
print(after_callable(sum, printAfter));
std::cout << "\nDecorated Before and After:\n";
print(decorate_callable(sum, printBefore, printAfter));
struct {
void operator()() const {}
} retVoid{};
const auto &voidDecorated = decorate_callable(retVoid,
[](const auto &...){},
[](const auto &...){});
// voidDecorated(); // does not compile
return 0;
}
https://godbolt.org/z/x94ehTq17
- 什么是處理物件
void回傳型別的簡單且優化友好的方法decorated? - 我如何可以選擇性地忽略不需要的 lambda 引數?使用可變引數 lambda 是一種方法,但它是在用戶端強制執行的。
- 我應該
decltype(auto)在函式宣告中使用 return 嗎?
uj5u.com熱心網友回復:
我正在使用 Andrei Alexandrescus 的 SCOPE_EXIT 宏來討論宣告式控制流。這里的技巧是 SCOPE_EXIT 創建一個帶有 lambda 的物件(以下塊)并在解構式中執行 lambda。這會延遲執行,直到控制流退出塊。
SCOPE_EXIT 將始終執行代碼,SCOPE_SUCCESS 僅在未引發例外時執行代碼,SCOPE_FAIL 僅在引發例外時執行代碼。
注意:當拋出例外時,原始裝飾器沒有執行 callAfter 。
#include <utility>
#include <exception>
#ifndef CONCATENATE_IMPL
#define CONCATENATE_IMPL(s1, s2) s1##s2
#endif
#ifndef CONCATENATE
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#endif
#ifndef ANONYMOUS_VARIABLE
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
#endif
template <typename FunctionType>
class ScopeGuard {
FunctionType function_;
public:
explicit ScopeGuard(const FunctionType& fn) : function_(fn) { }
explicit ScopeGuard(const FunctionType&& fn) : function_(std::move(fn)) { }
~ScopeGuard() noexcept {
function_();
}
};
enum class ScopeGuardOnExit { };
template <typename Fun>
ScopeGuard<Fun> operator (ScopeGuardOnExit, Fun&& fn) {
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ScopeGuardOnExit() [&]()
template <typename DecoratedT, typename CallableT>
constexpr auto after_callable(DecoratedT &&decorated, CallableT &&callAfter)
{
return [decorated = std::forward<DecoratedT>(decorated),
callAfter = std::forward<CallableT>(callAfter)](auto &&...args){
SCOPE_EXIT {
callAfter(std::as_const(args)...); // TODO: ignore parameters?
};
auto &&res = decorated(std::forward<decltype(args)>(args)...);
return res;
};
}
uj5u.com熱心網友回復:
根據 Goswin 的建議,我想出了一個更短更簡潔的版本:
template <typename DecoratedT, typename CallableT>
constexpr decltype(auto) after_callable(DecoratedT &&decorated, CallableT &&callAfter)
{
return [decorated = std::forward<DecoratedT>(decorated),
callAfter = std::forward<CallableT>(callAfter)](auto &&...args){
const auto &callOnExit = [&](){
callAfter(std::as_const(args)...); // TODO: ignore parameters?
};
struct on_exit {
decltype(callOnExit) onExit;
~on_exit() { !bool(std::uncaught_exceptions()) ? onExit() : void();}
} onExit{callOnExit};
return decorated(std::forward<decltype(args)>(args)...);
};
}
可變引數捕獲只能從 C 20 獲得(這個問題很有用)。但是,由于包裝 lambda 引數的callOnExit 壽命不長(如果我錯了,請糾正我),通過參考捕獲所有引數是安全的。
https://godbolt.org/z/dEq19EjWo
一種天真的方法是檢查回傳型別if constexpr
template<typename DecoratedT, typename CallableT>
constexpr decltype(auto) before_callable(DecoratedT &&decorated, CallableT &&callBefore)
{
return [decorated = std::forward<DecoratedT>(decorated),
callBefore = std::forward<CallableT>(callBefore)](auto &&...args) {
callBefore(std::as_const(args)...); // TODO: ignore parameters?
return decorated(std::forward<decltype(args)>(args)...);
};
}
template<typename DecoratedT, typename CallableT>
constexpr decltype(auto) after_callable(DecoratedT &&decorated, CallableT &&callAfter)
{
return [decorated = std::forward<DecoratedT>(decorated),
callAfter = std::forward<CallableT>(callAfter)](auto &&...args) {
constexpr auto ret_type_is_void =
std::is_void_v<decltype(decorated(std::forward<decltype(args)>(args)...))>;
if constexpr (!ret_type_is_void)
{
auto &&res = decorated(std::forward<decltype(args)>(args)...);
callAfter(std::as_const(args)...); // TODO: ignore parameters?
return res;
}
else
{
decorated(std::forward<decltype(args)>(args)...);
callAfter(std::as_const(args)...); // TODO: ignore parameters?
}
};
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/476407.html
