类 Python C++ 装饰器

Posted

技术标签:

【中文标题】类 Python C++ 装饰器【英文标题】:Python-like C++ decorators 【发布时间】: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)


有可能吗?

【问题讨论】:

【参考方案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 依赖项很好,因为它们保证作为...标准安装的一部分存在。

以上是关于类 Python C++ 装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python 装饰器:装饰器实例类装饰器(装饰函数)

python 装饰器:装饰器实例类装饰器(装饰函数)

Python学习笔记012——装饰器

使用装饰器作为类来装饰 Python 类

PYTHON里的装饰器能装饰类吗

Python入门之python装饰器的4种类型:函数装饰函数函数装饰类类装饰函数类装饰类