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?
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);
}
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);
}
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.
All the answers above are complicated and uses libraries.
My answer here is by far the most simple and doesn't need any library header.
// "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
Now in the Implementation file do the following
// "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;
}
End of the story.
Now this is how to use it in your code.
// "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;
}
This is it Folks!
Pros:
The CLASS "deco" has only one data member => small memory foot print
the operator() takes any number of arguments, so you can decorate any function regardless of its number of arguments.
Simple implementation => simple debugging and testing.
Cons: