Home > Enterprise >  C template class error: function returning a function
C template class error: function returning a function

Time:07-26

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:

  1. The Logger3 template class 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).
    Therefore instantiating the Logger3 in your case of a function that gets 2 ints and returns an int should be:
auto caller = Logger3<int, int, int>(add, "test");
  1. Your Logger3 constructor should be public in order to invoke it from outside the class.

  2. 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).

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)>.

  • Related