【问题标题】:How to make a template Wrapper/Decorator in c++17如何在 C++17 中制作模板包装器/装饰器
【发布时间】:2020-11-03 15:56:54
【问题描述】:

尊敬的 Stackoverflow 社区,

我对 c++ 还很陌生,而且我一直在摸索,还没有找到解决问题的方法。一段时间以来,我一直在寻找和尝试一些事情,并且已经到了提出问题会更有益和更有教育意义的地步。

问题:

我想创建一个类或函数来包装/装饰带有或不带参数的给定函数。 就像 python 或 c# 等中的老式 @wrapthis 一样。

到目前为止,我发现的最接近的东西(即优雅、简短且易于使用)来自这个 stackoverflow 答案:C++ Equivalent Decorator

挠头部分正在尝试传递指针函数。我收到的错误:

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 [再次来源:C++ Equivalent Decorator]

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;
}

ma​​in.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;
}

谢谢!

编辑:解决方案

根据下面给出的友好答案,我想用一个例子来编辑这篇文章。问题的解决方案是使用 lambda。

Decorator.h:我创建了 2 个装饰器(一个用于返回函数,一个用于 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;
}

输出

开始返回装饰...
END RETURN 装修
255

开始 VOID 装饰...
v_func_with_one_arg2
END VOID装饰

我希望这可以帮助其他人。

【问题讨论】:

  • 你需要一个对象来调用一个成员函数。现在有几个选择:是要将实例包装到somedeco 中,还是somedeco 在调用时应将实例作为参数?
  • @idclev463035818 不是我的对象的“东西”吗?但是要回答您的问题,可能会将实例包装到 somedeco 中?因为从视觉角度来看,知道它返回的数据类型有点不清楚。不管怎样,你有什么建议?
  • 对象中的函数是静态的,它们没有不同的每个对象指针。
  • @MichaelChourdakis 非静态成员函数可以,即this,但要做到这一点,您必须首先进入函数,这需要首先在给定实例上调用它;-)我认为您的观点是获取指向成员函数的指针的语法宁愿是&amp;Something::return_func_with_one_arg,即指针本身没有附加实例,需要在调用时提供。
  • 另外,正如@idclev463035818 指出的那样,new 在这里没有合法用途。更喜欢auto something = Something{};

标签: c++ pointers decorator function-pointers wrapper


【解决方案1】:

没有简单的 1:1 替代 C++ 中 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 创建对象。

【讨论】:

  • lambda 确实是解决方案。谢谢!
【解决方案2】:

调用decorator(&amp;something-&gt;return_func_with_one_arg) 无效。在 C++ 中没有指向绑定函数的指针。

例如,如果您希望 somedeco 成为一个类似函数的对象来包装对 something-&gt;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 返回的对象的寿命更长。

【讨论】:

  • lambda 确实是解决方案。谢谢!
【解决方案3】:

你需要绑定它

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-20
    • 2021-05-20
    • 2013-06-12
    • 1970-01-01
    相关资源
    最近更新 更多