I am looking for a way to decorate functions or lambdas in C++. The goal is to do something before and after the function call. As I've seen the closest thing to use is std::function but it needs to have the types of its arguments.
class FunctionDecorator
{
public:
FunctionDecorator( std::function func )
: m_func( func )
void operator()()
{
// do some stuff prior to function call
m_func();
// do stuff after function call
}
private:
std::function m_func;
};
It would be great if by template type could be used in std::function and it could deduce it somehow when i pass pointer to a function or a result from std::bind. Is such thing possible in C++.
Hmm. I may or may not have gone overkill.
#include <type_traits>
#include <utility>
#include <iostream>
template <class T>
struct RetWrapper {
template <class Tfunc, class... Targs>
RetWrapper(Tfunc &&func, Targs &&... args)
: val(std::forward<Tfunc>(func)(std::forward<Targs>(args)...)) {}
T &&value() { return static_cast<T &&>(val); }
private:
T val;
};
template <>
struct RetWrapper<void> {
template <class Tfunc, class... Targs>
RetWrapper(Tfunc &&func, Targs &&... args) {
std::forward<Tfunc>(func)(std::forward<Targs>(args)...);
}
void value() {}
};
template <class Tfunc, class Tbefore, class Tafter>
auto decorate(Tfunc &&func, Tbefore &&before, Tafter &&after) {
return [
func = std::forward<Tfunc>(func),
before = std::forward<Tbefore>(before),
after = std::forward<Tafter>(after)
] (auto &&... args) -> decltype(auto) {
before(std::forward<decltype(args)>(args)...);
RetWrapper<std::result_of_t<Tfunc(decltype(args)...)>> ret(
func, std::forward<decltype(args)>(args)...
);
after(std::forward<decltype(args)>(args)...);
return ret.value();
};
}
/*
* Tests
*/
float test1(float a, float b) {
std::cout << "Inside test1\n";
return a * b;
}
void test2() {
std::cout << "Inside test2\n";
}
int i = 0;
int &test3() {
return i;
}
int main() {
auto test1Deco = decorate(
test1,
[] (float a, float b) {
std::cout << "Calling test1 with " << a << " and " << b << '\n';
},
[] (float a, float b) {
std::cout << "Called test1 with " << a << " and " << b << '\n';
}
);
float c = test1Deco(3.5f, 5.1f);
std::cout << "Yields " << c << '\n';
auto test2Deco = decorate(
test2,
[] () {
std::cout << "Calling test2\n";
},
[] () {
std::cout << "Called test2\n";
}
);
test2Deco();
auto test3Deco = decorate(
test3,
[] () {
std::cout << "Calling test3\n";
},
[] () {
std::cout << "Called test3\n";
}
);
auto &i2 = test3Deco();
i2 = 42;
std::cout << "Global i = " << i << '\n';
return 0;
}
Output :
Calling test1 with 3.5 and 5.1
Inside test1
Called test1 with 3.5 and 5.1
Yields 17.85
Calling test2
Inside test2
Called test2
Calling test3
Called test3
Global i = 42
Just go full template, without std::function:
template< typename Func >
class FunctionDecorator
{
public:
FunctionDecorator( Func func )
: m_func( std::move(func) )
{}
void operator()()
{
// do some stuff prior to function call
m_func();
// do stuff after function call
}
private:
Func m_func;
};
template< typename Func >
FunctionDecorator<Func> decorate(Func func) {
return FunctionDecorator<Func>(std::move(func));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With