web-dev-qa-db-fra.com

Décorateurs C ++ de type Python

Existe-t-il des façons de décorer des fonctions ou des méthodes en C++ comme dans le style python?

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

Utiliser des macros par exemple:

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

ou

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

C'est possible?

30
Artem Selivanov

std::function fournit la plupart des éléments constitutifs de ma solution proposée.

Voici ma solution proposée.

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

Production:

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

[~ # ~] ps [~ # ~]

Decorator fournit un endroit où vous pouvez ajouter des fonctionnalités au-delà de l'appel de fonction. Si vous souhaitez passer simplement à std::function, vous pouvez utiliser:

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

Voici ma tentative. Fonctionne sous C++ 14 (lambdas génériques et déduction de type de retour).

#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);
}
9
thorhunter

Vous pouvez obtenir des fonctionnalités limitées de ce type en utilisant l'opérateur de prétraitement de collage de jetons ##. Voir https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html . La difficulté est qu'en C chaque nom de fonction doit être défini au moment du lien, donc les fonctions ne sont pas des objets qui peuvent être transformés comme Python le fait. Donc en Python les décorateurs sont utiles et de bon style, mais en C, ces astuces doivent être utilisées avec modération, voire pas du tout.

2
Paul Cornelius