具有条件 return 的可调用对象的通用装饰器
generic decorators for callable objects with conditional return
我想为可调用对象编写装饰函数。
这是我现在拥有的:
#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
对象的 return 类型不是 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
- 从
decorated
对象处理 void
return 类型的简单且优化友好的方法是什么?
- 如何选择性地忽略不需要的 lambda 参数?使用可变 lambda 是一种方式,但它在用户端强制执行。
- 我应该在函数声明中对 return 使用
decltype(auto)
吗?
根据 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 获得(这个 很有用)。然而,由于 callOnExit
不会比 (如果我错了请纠正我)包装 lambda 的参数,通过引用捕获所有参数是安全的。
https://godbolt.org/z/dEq19EjWo
一种天真的方法是用 if constexpr
检查 return 类型
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?
}
};
}
我正在使用 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;
};
}
我想为可调用对象编写装饰函数。
这是我现在拥有的:
#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
对象的 return 类型不是 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
- 从
decorated
对象处理void
return 类型的简单且优化友好的方法是什么? - 如何选择性地忽略不需要的 lambda 参数?使用可变 lambda 是一种方式,但它在用户端强制执行。
- 我应该在函数声明中对 return 使用
decltype(auto)
吗?
根据 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 获得(这个 callOnExit
不会比 (如果我错了请纠正我)包装 lambda 的参数,通过引用捕获所有参数是安全的。
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?
}
};
}
我正在使用 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;
};
}