如何在 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;
}

https://godbolt.org/z/zdYW9q