I want to make a simple logger which automatically runs a function and returns its value.
The class is defined as:
template <typename R, typename... Args>
class Logger3
{
Logger3(function<R(Args...)> func,
const string& name):
func{func},
name{name}
{}
R operator() (Args ...args)
{
cout << "Entering " << name << endl;
R result = func(args...);
cout << "Exiting " << name << endl;
return result;
}
function<R(Args...)> func;
string name;
};
I want to pass the following simple add
function to the logger:
int add(int a, int b)
{
cout<<"Add two value"<<endl;
return a+b;
}
By calling it this way:
auto caller = Logger3<int(int,int)>(add,"test");
However, it generates the following errors:
error: function returning a function
133 | Logger3(function<R(Args...)> func,
| ^~~~~~~
decorator.h:138:7: error: function returning a function
138 | R operator() (Args ...args)
| ^~~~~~~~
decorator.h:145:26: error: function returning a function
145 | function<R(Args...)> func;
There are 3 issues in your code:
Logger3
class template requires R
to be the return value of the function (and Args
it's arguments).R
is not a function type as implied by your attempt to instantiate Logger3
).Logger3
in your case of a function that gets 2 int
s and returns an int
should be:auto caller = Logger3<int, int, int>(add, "test");
Your Logger3
constructor should be public in order to invoke it from outside the class.
For efficiency reasons, you should use std::forward
to forward the arguments from operator()
to your function. This will avoid copy of the arguments (more significant in cases where their types are more complex than int
s).
Note that in order for std::forward
to work as expected, operator()
has to be itself a variadic template using forwarding references (see below).
Complete fixed version:
#include <string> // std::string
#include <functional> // std::function
#include <utility> // std::forward, std::declval
#include <iostream> // std::cout
template <typename R, typename... Args>
class Logger3
{
public:
Logger3(std::function<R(Args...)> func,
const std::string& name) :
func{ func },
name{ name }
{}
// Template with forwarding references to avoid copies
// 'typename' arg is for SFINAE, and only enables if a
// function accepting 'Args...' can evaluate with 'UArgs...'
template <typename...UArgs,
typename = decltype(std::declval<R(*)(Args...)>()(std::declval<UArgs>()...))>
R operator() (UArgs&&...args)
{
std::cout << "Entering " << name << std::endl;
R result = func(std::forward<UArgs>(args)...);
std::cout << "Exiting " << name << std::endl;
return result;
}
private:
std::function<R(Args...)> func;
std::string name;
};
int add(int a, int b)
{
std::cout << "Add two value" << std::endl;
return a + b;
}
int main()
{
auto caller = Logger3<int, int, int>(add, "test");
auto res = caller(3, 4);
std::cout << "result: " << res << std::endl;
return 0;
}
Output:
Entering test
Add two value
Exiting test
result: 7
Demo: Godbolt.
A side note: better to avoid using namespace std
- see here: Why is "using namespace std;" considered bad practice?.