【问题标题】:Python-like C++ decorators类 Python C++ 装饰器
【发布时间】:2015-08-21 03:59:37
【问题描述】:

有没有办法像python风格一样在C++中装饰函数或方法?

@decorator
def decorated(self, *args, **kwargs):
     pass

以宏为例:

DECORATE(decorator_method)
int decorated(int a, float b = 0)
{
    return 0;
}

DECORATOR_MACRO
void decorated(mytype& a, mytype2* b)
{
}

有可能吗?

【问题讨论】:

    标签: c++ function macros decorator


    【解决方案1】:

    std::function 为我提出的解决方案提供了大部分构建块。

    这是我提出的解决方案。

    #include <iostream>
    #include <functional>
    
    //-------------------------------
    // BEGIN decorator implementation
    //-------------------------------
    
    template <class> struct Decorator;
    
    template <class R, class... Args>
    struct Decorator<R(Args ...)>
    {
       Decorator(std::function<R(Args ...)> f) : f_(f) {}
    
       R operator()(Args ... args)
       {
          std::cout << "Calling the decorated function.\n";
          return f_(args...);
       }
       std::function<R(Args ...)> f_;
    };
    
    template<class R, class... Args>
    Decorator<R(Args...)> makeDecorator(R (*f)(Args ...))
    {
       return Decorator<R(Args...)>(std::function<R(Args...)>(f));
    }
    
    //-------------------------------
    // END decorator implementation
    //-------------------------------
    
    //-------------------------------
    // Sample functions to decorate.
    //-------------------------------
    
    // Proposed solution doesn't work with default values.
    // int decorated1(int a, float b = 0)
    int decorated1(int a, float b)
    {
       std::cout << "a = " << a << ", b = " << b << std::endl;
       return 0;
    }
    
    void decorated2(int a)
    {
       std::cout << "a = " << a << std::endl;
    }
    
    int main()
    {
       auto method1 = makeDecorator(decorated1);
       method1(10, 30.3);
       auto method2 = makeDecorator(decorated2);
       method2(10);
    }
    

    输出:

    Calling the decorated function.
    a = 10, b = 30.3
    Calling the decorated function.
    a = 10
    

    附言

    Decorator 提供了一个地方,您可以在其中添加函数调用之外的功能。如果你想简单地传递给std::function,你可以使用:

    template<class R, class... Args >
    std::function<R(Args...)> makeDecorator(R (*f)(Args ...))
    {
       return std::function<R(Args...)>(f);
    }
    

    【讨论】:

    • 漂亮的代码,但是太长了。我明白,这不是 Py,而且 C++ 没有像 Py 那样提供强大的语法糖。 :) 但是有办法声明一些宏并使用我在示例中显示的代码吗?这个装饰方法在运行时定义,但我想在所有其他函数和类中使用它。我必须到处重复“makeDecorator”吗?谢谢。 :)
    • @Broly,是的,对于要装饰的每个函数,您必须至少重复一次对 makeDecorator() 的调用。为了使建议的代码生产质量,您需要做一些工作。
    【解决方案2】:

    这是我的尝试。在 C++14 下工作(通用 lambda 和返回类型推导)。

    #include <iostream>
    #include <functional>
    
    /* Decorator function example,
       returns negative (! operator) of given function
    */
    template <typename T>
    auto reverse_func(T func)
    {
        auto r_func =
        [=](auto ...args)
        { 
            return !func(args...); 
        };
    
        return r_func; 
    }
    
    /* Decorator function example,
       prints result of given function before it's returned
    */
    template <typename T>
    auto print_result_func(T func)
    {
        auto r_func = 
        [=](auto ...args)
        {
            auto result = func(args...);
            std::cout << "Result: " << result << std::endl;
            return result;
        };
    
        return r_func;
    }
    
    /* Function to be decorated example,
       checks whether two given arguments are equal
    */
    bool cmp(int x, int y)
    {
        return x == y;
    }
    
    /* Decorator macro */
    #define DECORATE(function, decorator) \
        decorator<decltype(function)>(function)
    
    int main()
    {
        auto reversed = DECORATE(cmp, reverse_func);
        auto print_normal = DECORATE(cmp, print_result_func);
        auto print_reversed = DECORATE(reversed, print_result_func);
        auto print_double_normal = DECORATE(print_normal, print_result_func);
        auto print_double_reversed = DECORATE(print_reversed, print_result_func);
    
        std::cout << cmp(1,2) << reversed(1,2) << std::endl;
        print_double_normal(1,2);
        print_reversed(1,2);
        print_double_reversed(1,2);
    }
    

    【讨论】:

    • 很好,但不适用于非静态函数,有没有办法解决这个问题?
    • @pholat 你可以像这样包装你的函数:MyClass non; auto cmp_wrapper = [&amp;](auto ...args){ return non.cmp(args...);};
    【解决方案3】:

    这是 github 上的一个项目,它几乎是关于如何在 C++14 及更高版本中实现此行为的简短教程。这是一个非常灵活的设计,也可以装饰非静态功能。作者也没有使用任何复杂的东西:

    https://github.com/TheMaverickProgrammer/C-Python-like-Decorators

    【讨论】:

      【解决方案4】:

      您可以使用标记粘贴预处理运算符## 获得这种类型的一些有限功能。见https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html。困难在于,在 C 中,每个函数名都必须在链接时定义,因此函数不是可以像 Python 那样转换的对象。所以在 Python 中,装饰器是有用且良好的风格,但在 C 中,如果有的话,应该谨慎使用这些技巧。

      【讨论】:

        【解决方案5】:

        以上所有答案都很复杂并且使用库。 我的回答是迄今为止最简单的,不需要任何库头。

            // "DECORATOR.h"
            #pragma once
            #ifndef DECORATOR_H
            #define DECORATOR_H
        
            template<typename T>
            class deco
            {
                T* m_func;
             public:
                explicit deco(T func);
        
                template<typename ...args>
                auto operator()(args... Args);
            }
            #endif // DECORATOR_H
        

        现在在实现文件中执行以下操作

           // "DECORATOR.cpp"
           template<typename T>
           inline deco<T>::deco(T func)
           :m_func(func)
           {
           };
        
           // implementing the function call operator
           template <typename T>
           template <typename ...args>
           auto deco<T>::operator()(args ...Args)
           {
               //Do some stuff defore the decorated function call
               // ....
               // Call the decorated function.
               auto rv = m_func(Args...);
        
               //Do some stuff after the function call
               // ....
               return rv;
           }
        

        故事结束。 现在这是在您的代码中使用它的方法。

            // "main.cpp"
            #include "DECORATOR.h"
            #include <stdio.h>  // just for printf()
        
            // functions to decorate
            int add(int a, int b)
            {
                return a+b;
            };
        
            int sub(int a, int b)
            {
                return a-b;
            };
        
            // Main function
            int main()
            {
                // decorate the functions "add", "sub"
                deco<decltype(add)> add_Deco(add);
                deco<decltype(sub)> sub_Deco(sub);
        
                // call your decorated functions
                printf("result of decorated Add =%d\n", add_Deco(5,2));
                printf("result of decorated Sub =%d\n", sub_Deco(4,3));
                return 0;
            }
        

        这就是伙计们!

        优点:

        • “deco”类只有一个数据成员 => 内存占用小

        • operator() 接受任意数量的参数,因此您可以装饰任何函数而不管其参数数量。

        • 简单的实现 => 简单的调试和测试。

        缺点:

        • 未知!

        【讨论】:

        • 缺点:无效的 C++,错误的包含保护,printf 在非 POD 上产生未定义的行为。顺便说一句,函数定义不会被;终止。
        • “我这里的答案……不需要任何库头”但是从最终用户的角度来看它是一个库头,所以看起来有点自我-反驳。另外,为什么不需要库头是一件好事?如果您在谈论非std 依赖项,我可以理解,但std 依赖项很好,因为它们保证作为...标准安装的一部分存在。
        猜你喜欢
        • 2011-04-28
        • 2014-02-16
        • 1970-01-01
        • 2021-07-16
        • 1970-01-01
        • 2010-12-19
        • 2019-09-20
        • 2011-11-21
        • 2010-10-14
        相关资源
        最近更新 更多