Python-like C++ decorators
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
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);
}
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);
}