43

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?

wjandrea
34.1k10 gold badges69 silver badges107 bronze badges
asked Jun 6, 2015 at 5:53
1

6 Answers 6

40

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);
}
answered Jun 6, 2015 at 6:56
Sign up to request clarification or add additional context in comments.

2 Comments

Pretty code, but too long. I understand, this is not Py, and C++ not provided strong syntax sugar like Py. :) But there way to declare some macros and use your code as I show in my examples? This decorated method defined in runtime, but I want to use it in all other functions and classes. I must repeat "makeDecorator" everywhere? Thanks. :)
@Broly, yes, you'll have to repeat the calls to makeDecorator() at least once for each function that you want to decorate. To make the suggested code production quality, you'll need a bit of work.
10

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);
}
answered Oct 20, 2016 at 13:01

2 Comments

Neat, but won't work on non static functions, is there a way to solve that?
@pholat You can wrap your function like this: MyClass non; auto cmp_wrapper = [&](auto ...args){ return non.cmp(args...);};
4

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

answered Dec 11, 2019 at 5:53

Comments

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.

answered Jun 6, 2015 at 6:46

Comments

1

Inspired by @R Sahu answer here, I solved it using macro and variadic args, one of the feature python decorators have is implicit decoration using @ syntax as opposed to calling make_decorator function in every scope. Using macro adds the extra syntactic sugar, my attempt:

#define DECORATOR(Class, ReturnType, function, arguments, before, after) \
template <typename Base> \
class DecoratorCls##function {public: \
 Base* self; \
 DecoratorCls##function(Base* object): self(object) {;} \
 template <typename ... Args> \
 ReturnType operator()(Args... args) { \
 before(args...); \
 ReturnType ans = self -> Decorator##function(args...); \
 ReturnType processed_ans = after(ans); \
 return processed_ans; \
 } \
}; \
DecoratorCls##function<Class> function = DecoratorCls##function<Class>(this); \
ReturnType Decorator##function arguments

Usage:

void before_decoration(int &a1, int &a2) {
 std::cout << "Berfore decoration with: " << a1 << " " << a2 << std::endl;
 // a1 += 2; // Args can be processed if required
}
int after_decoration(int ans) {
 std::cout << "After decoration" << std::endl;
 return ans;
}
class MyClass {
 private:
 int private_variable;
 public:
 MyClass(int pv): private_variable(pv) {
 std::cout << "Counstructor" << std::endl;
 }
 DECORATOR(MyClass, int, method, (int a, int b), before_decoration, after_decoration) {
 std::cout << "Decorated method: " << std::endl;
 std::cout << "Class data member: " << private_variable << std::endl;
 std::cout << "method args: " << a << " " << b << std::endl;
 return private_variable + a + b;
 }
};
int main(int argc, char *argv[]) {
 MyClass object = MyClass(10);
 std::cout << "Member function Call: " << std::endl << object.method(4, 25) << std::endl;
 return 0;
}

can be modified for decorating static function.

Assumptions:

  • Appropriate standard/version available for variadic args and inline initialization of class data member.
  • Inner class automatically friend to outer class.
  • before and after functions are assumed to be static functions but code can be modified to member functions of example class as self -> before(args...)

I know some assumptions have overlapping scope, mentioned for additional clarity

answered Aug 26, 2023 at 12:05

Comments

-6

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:

  • none known!
Pang
10.2k146 gold badges87 silver badges126 bronze badges
answered Sep 5, 2017 at 22:02

2 Comments

Cons: Invalid C++, bad include guards, printf yields undefined behaviour on non-PODs. And by the way, function definitions are not terminated by ;.
"My answer here ... doesn't need any library header" But it is a library header from the final user's point of view, so it seems kind of self-refuting. Also, why is not needing a library header a good thing? I can understand if you were talking about non-std dependencies, but std dependencies are fine, since they're guaranteed to exist as part of the, well... standard, installation.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.