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;
CodePudding user response:
There are 3 issues in your code:
- The
Logger3
template class requiresR
to be the return value of the function (andArgs
it's arguments).
(R
is not a function type as implied by your attempt to instantiateLogger3
).
Therefore instantiating theLogger3
in your case of a function that gets 2int
s and returns anint
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 fromoperator()
to your function. This will avoid copy of the arguments (more significant in cases where their types are more complex).
Complete fixed version:
#include <iostream>
#include <string>
#include <functional>
template <typename R, typename... Args>
class Logger3
{
public:
Logger3(std::function<R(Args...)> func,
const std::string& name) :
func{ func },
name{ name }
{}
R operator() (Args ...args)
{
std::cout << "Entering " << name << std::endl;
R result = func(std::forward<Args>(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
A side note: better to avoid using namespace std
- see here Why is "using namespace std;" considered bad practice?.
CodePudding user response:
You need to use template class partial specialization to get the type of R
.
template <typename F>
class Logger3;
template <typename R, typename... Args>
class Logger3<R(Args...)>
{
// implementation details
};
which makes int
match the template parameter R
of the partial specialization when you explicitly specify Logger3<int(int,int)>
.