如何在 C++17 中制作模板 Wrapper/Decorator
How to make a template Wrapper/Decorator in c++17
亲爱的 Whosebug 社区,
我对 C++ 还是有点陌生,我一直在摸不着头脑,还没有找到解决我问题的方法。我已经搜索和尝试了一段时间,现在我已经到了提出问题会更有益和更有教育意义的地步。
问题:
我想创建一个 class 或 wraps/decorates 给定函数,带或不带参数。
就像 python 或 c# 之类的老式 @wrapthis。
到目前为止我发现的最接近的东西(优雅、简短且易于使用)来自这个 Whosebug 答案:
挠头的部分是试图传递一个指针函数。我收到的错误:
Error (active) E0300 a pointer to a bound function may only be used to call the function
这显然意味着不允许以某种方式以这种方式传递指针,那么我在这里有什么选择?
一个工作示例作为答案会很棒!
可以在下面找到我正在努力实现的示例:
Something.h
class Something
{
private:
public:
Something() {}
void v_func_with_nothing() { std::cout << "v_func_with_nothing" << "\n"; }
void v_func_with_void(void) { std::cout << "v_func_with_void" << "\n"; }
void v_func_with_one_arg(int x) { std::cout << "v_func_with_one_arg" << x << " " << "\n"; }
void v_func_with_args(int x, int y) { std::cout << "v_func_with_args" << x << " " << y << "\n"; }
int return_func_with_nothing() { return 1; }
int return_func_with_void(void) { return 3; }
int return_func_with_one_arg(int x) { return x; }
int return_func_with_args(int x, int y) { return x+y; }
};
Decorator.h [再次来源:]
template<typename T>
auto decorator(T&& func)
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
main.cpp
#include <iostream>
#include "Something.h"
#include "Decorator.h"
int main()
{
Something* something = new Something();
auto somedeco = decorator(&something->return_func_with_one_arg);//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
谢谢!
编辑:解决方案
根据下面给出的友好回答,我想用一个例子来编辑这个 post。问题的解决方案是使用 lambda。
Decorator.h:我创建了 2 个装饰器(一个用于 return-函数,一个用于 void-函数):
template<typename T>
auto DECO_R(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN RETURN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END RETURN decorating\n";
return result;
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
template<typename T>
auto DECO_V(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN VOID decorating...\n";
func(std::forward<decltype(args)>(args)...);
std::cout << "END VOID decorating\n";
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
Main.cpp: 2 个例子
int main()
{
Something* something = new Something();
auto somedeco = DECO_R(
[&](int x) {
return something->return_func_with_one_arg(x);
});
int value = somedeco(255);
std::cout << value << "\n";
auto some_v_deco = DECO_V(
[&](int x) {
return something->v_func_with_one_arg(x);
});
some_v_deco(2);
return 0;
}
输出
开始 RETURN 装饰...
结束 RETURN 装饰
255
开始作废装饰...
v_func_with_one_arg2
END VOID 装饰
我希望这对其他人有帮助。
在 C++ 中没有简单的 1:1 替代 Python 的动态类型。您的示例无法编译,因为没有指向某个特定实例的成员函数的指针。指向成员函数的指针总是需要一个实例来调用。对于一个简单的案例,我建议使用 lambda:
int main() {
Something something;
auto somedeco = [&](auto param) {
// before call
auto v = something.return_func_with_one_arg(param);
// after call
return v;
};
return somedeco(1);
}
如您所见,decorate
的整个机制并不是真正需要的,因为使用 lamdda,您可以内联编写包装函数。另一方面,decorator
允许您为不同的方法重用 // before call
和 // after call
。要修复您的代码,您还可以将 lambda 传递给 decorate
.
PS: 不要使用new
创建对象。
调用decorator(&something->return_func_with_one_arg)
无效。 C++ 中没有指向绑定函数的指针。
例如,如果您希望 somedeco
成为包装对 something->return_func_with_one_arg(42)
的调用的类函数对象,您需要将调用包装在 lambda 中:
auto somedeco = decorator(
[&]() {
return something->return_func_with_one_arg(42);
}
);
somedeco();
或者你可以通过装饰器传递参数:
auto somedeco = decorator(
[&](int x) {
return something->return_func_with_one_arg(x);
}
);
somedeco(42);
请记住,这将要求 something
指向的对象比 decorator
返回的对象长寿。
你需要绑定它
int main()
{
Something* something = new Something();
using std::placeholders::_1;
auto f = std::bind( &Something::return_func_with_one_arg, something, _1 );
auto somedeco = decorator( f );//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
亲爱的 Whosebug 社区,
我对 C++ 还是有点陌生,我一直在摸不着头脑,还没有找到解决我问题的方法。我已经搜索和尝试了一段时间,现在我已经到了提出问题会更有益和更有教育意义的地步。
问题:
我想创建一个 class 或 wraps/decorates 给定函数,带或不带参数。 就像 python 或 c# 之类的老式 @wrapthis。
到目前为止我发现的最接近的东西(优雅、简短且易于使用)来自这个 Whosebug 答案:
挠头的部分是试图传递一个指针函数。我收到的错误:
Error (active) E0300 a pointer to a bound function may only be used to call the function
这显然意味着不允许以某种方式以这种方式传递指针,那么我在这里有什么选择?
一个工作示例作为答案会很棒!
可以在下面找到我正在努力实现的示例:
Something.h
class Something
{
private:
public:
Something() {}
void v_func_with_nothing() { std::cout << "v_func_with_nothing" << "\n"; }
void v_func_with_void(void) { std::cout << "v_func_with_void" << "\n"; }
void v_func_with_one_arg(int x) { std::cout << "v_func_with_one_arg" << x << " " << "\n"; }
void v_func_with_args(int x, int y) { std::cout << "v_func_with_args" << x << " " << y << "\n"; }
int return_func_with_nothing() { return 1; }
int return_func_with_void(void) { return 3; }
int return_func_with_one_arg(int x) { return x; }
int return_func_with_args(int x, int y) { return x+y; }
};
Decorator.h [再次来源:
template<typename T>
auto decorator(T&& func)
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
main.cpp
#include <iostream>
#include "Something.h"
#include "Decorator.h"
int main()
{
Something* something = new Something();
auto somedeco = decorator(&something->return_func_with_one_arg);//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
谢谢!
编辑:解决方案
根据下面给出的友好回答,我想用一个例子来编辑这个 post。问题的解决方案是使用 lambda。
Decorator.h:我创建了 2 个装饰器(一个用于 return-函数,一个用于 void-函数):
template<typename T>
auto DECO_R(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN RETURN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END RETURN decorating\n";
return result;
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
template<typename T>
auto DECO_V(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN VOID decorating...\n";
func(std::forward<decltype(args)>(args)...);
std::cout << "END VOID decorating\n";
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
Main.cpp: 2 个例子
int main()
{
Something* something = new Something();
auto somedeco = DECO_R(
[&](int x) {
return something->return_func_with_one_arg(x);
});
int value = somedeco(255);
std::cout << value << "\n";
auto some_v_deco = DECO_V(
[&](int x) {
return something->v_func_with_one_arg(x);
});
some_v_deco(2);
return 0;
}
输出
开始 RETURN 装饰...
结束 RETURN 装饰
255
开始作废装饰...
v_func_with_one_arg2
END VOID 装饰
我希望这对其他人有帮助。
在 C++ 中没有简单的 1:1 替代 Python 的动态类型。您的示例无法编译,因为没有指向某个特定实例的成员函数的指针。指向成员函数的指针总是需要一个实例来调用。对于一个简单的案例,我建议使用 lambda:
int main() {
Something something;
auto somedeco = [&](auto param) {
// before call
auto v = something.return_func_with_one_arg(param);
// after call
return v;
};
return somedeco(1);
}
如您所见,decorate
的整个机制并不是真正需要的,因为使用 lamdda,您可以内联编写包装函数。另一方面,decorator
允许您为不同的方法重用 // before call
和 // after call
。要修复您的代码,您还可以将 lambda 传递给 decorate
.
PS: 不要使用new
创建对象。
调用decorator(&something->return_func_with_one_arg)
无效。 C++ 中没有指向绑定函数的指针。
例如,如果您希望 somedeco
成为包装对 something->return_func_with_one_arg(42)
的调用的类函数对象,您需要将调用包装在 lambda 中:
auto somedeco = decorator(
[&]() {
return something->return_func_with_one_arg(42);
}
);
somedeco();
或者你可以通过装饰器传递参数:
auto somedeco = decorator(
[&](int x) {
return something->return_func_with_one_arg(x);
}
);
somedeco(42);
请记住,这将要求 something
指向的对象比 decorator
返回的对象长寿。
你需要绑定它
int main()
{
Something* something = new Something();
using std::placeholders::_1;
auto f = std::bind( &Something::return_func_with_one_arg, something, _1 );
auto somedeco = decorator( f );//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}