Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python-like C++ decorators

Are there ways to decorate functions or methods in C++ like in python style?

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

Using macros for example:

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

or

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

Is it possible?

like image 628
Artem Selivanov Avatar asked Jun 06 '15 05:06

Artem Selivanov


People also ask

Did Python use decorators?

Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of a function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped function, without permanently modifying it.

What is decorator in Python?

A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.

What are fancy decorators in Python?

Decorator in Python is an important feature used to add functionalities to an existing function, object, or code without modifying its structure permanently. It allows you to wrap a function to another function and extend its behavior.

Are there decorators in C++?

Decorator in C++Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.


4 Answers

std::function provides most of the building blocks for my proposed solution.

Here is my proposed solution.

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

Output:

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

PS

Decorator provides a place where you can add functionality beyond making the function call. If you want a simple pass through to std::function, you can use:

template<class R, class... Args >
std::function<R(Args...)> makeDecorator(R (*f)(Args ...))
{
   return std::function<R(Args...)>(f);
}
like image 98
R Sahu Avatar answered Oct 19 '22 19:10

R Sahu


Here is my attempt. Works under C++14 (generic lambdas and return type deduction).

#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);
}
like image 20
thorhunter Avatar answered Oct 19 '22 18:10

thorhunter


Here's a project on github that's pretty much a short tutorial on how to achieve this behavior for C++14 and up. It's a very flexible design and can decorate non-static functions as well. The author doesn't use anything complex either:

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

like image 4
Dan Avatar answered Oct 19 '22 18:10

Dan


You can get some limited functionality of this type using the token-pasting pre-processing operator ##. See https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html. The difficulty is that in C every function name must be defined at link time, so functions are not objects that can be transformed like Python does. So in Python decorators are useful and good style, but in C such tricks should be used sparingly if at all.

like image 2
Paul Cornelius Avatar answered Oct 19 '22 18:10

Paul Cornelius