1

C++

I'm trying to implement a function wrapper via a (function object) class (variadic) template. The class has as its only data member a function pointer that is initialized by or assigned the function pointer it is wrapping. The parametrized constructor takes a function pointer and initializes the member by it. The operator() method takes argument(s) (or none) and calls the wrapped function with them. At least that's the idea. I get many errors, which I mark with comments. VC11 (with the November 2012 CTP, to enable variadic templates) gives me error C2091: function returns function in all but one of the marked areas. The last error is different, and I comment its full description in the code. g++ gives mostly the same errors, albeit with different code numbers.

#include <iostream>

template <typename R, typename... Tn>
class func
{
    R (*fptr)(Tn...); // C2091
public:
    func() : fptr(nullptr) {}
    func( R (*f) (Tn...) ) : fptr(f) {} // C2091
    R operator()(Tn... args)
    { // C2091
        return fptr(args...);
    }
    func& operator=( R (*f) (Tn...) ) // C2091
    {
        fptr = f;
        return *this;
    }
};

int foo(int a, int b)
{
    std::cout << "foo\n";
    return 0;
}

int main()
{
    func<int(int, int)> myfunc;
    myfunc = foo; // C2679: binary '=' : no operator found which takes
    // a right-hand operand of type 'int (__cdecl *)(int,int)' (or 
    // there is no acceptable conversion)
}

Why am I getting these errors? For example, I don't see how the parametrized constructor returns anything, or how the declaration of the data member returns anything. Isn't the data member declaration in the form of a function pointer declaration? For example, doesn't int (*g)(int); declare a pointer that points to a function that takes an int and returns an int?

Edit/Addendum:

I see from the answers that int(int, int) is only one type and that I need partial specialization to get the effect I want. But, what produces the error in my code? If I comment out myfunc = foo, I still get the other errors. func<int(int, int)> myfunc; calls the default constructor. typename R gets instantiated to int(int, int), and typename... Tn becomes empty. The data member R (*fptr)(Tn...); becomes R (*fptr)();, and fptr is therefore a function pointer that points to a function that takes zero arguments and returns an R. If R is int(int, int), then is R a function pointer type or a function type? If it's the latter, then I can understand the context of the error message.

CodeBricks
  • 1,771
  • 3
  • 17
  • 37

3 Answers3

2

Your class is parameterized by the return value and types of arguments, spelled out separately. But when instantiating, you try to parameterize it by a function type, a la std::function. Make it func<int, int, int> myfunc;. With this change, your code works.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • +1 for the complete working code, for seeing my intent of making the class template's usage style similar to `std::function`, and for pointing out the template must be instantiated with 3 template arguments instead of 1. – CodeBricks Oct 31 '13 at 09:44
2

int(int, int) is one single type. If you want to pass it like that and unwrap it, you need partial specialization:

template <typename> struct func;         // leave undefined

template <typename R, typename ...Args>
struct func<R(Args...)>                  // specialized for typename = R(Args...)
{
    // ...
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • +1 for "`int(int, int)` is one single type." I see your point in partial specialization. But what caused the error in my code? If I comment out the `myfunc = foo` line, I still get the other errors. In that case, the default constructor is called, the type of the data member becomes a function pointer to a function that returns a `int(int, int)` and takes zero arguments, am I right? Now, the part I’m really unsure about: by a function returning a `int(int, int)`, is it trying to return a function or a function pointer? – CodeBricks Oct 31 '13 at 00:52
  • @CodeBricks: In your code, `R = int(int, int)`, which is not a valid type for many of the other constructions in your class (e.g. functions cannot return functions). – Kerrek SB Oct 31 '13 at 08:48
  • Do you mean `int(int, int)` is a function type, not function pointer type? – CodeBricks Oct 31 '13 at 09:19
  • 1
    @CodeBricks: Well, yes, there's no `*` in it... The pointer type is `int(*)(int, int)` (which would be `R*` in your broken code). – Kerrek SB Oct 31 '13 at 09:21
2

You need partial specialization.

Here's a working example:

template <typename T>
class func;

template <typename R, typename... Tn>
class func<R(Tn...)> {
    typedef R (*fptr_t)(Tn...);
    fptr_t fptr;
public:
    func() : fptr(nullptr) {}
    func(fptr_t f) : fptr(f) {}
    R operator()(Tn... args) {
        return fptr(args...);
    }
    func& operator=(fptr_t f) {
        fptr = f;
        return *this;
    }
};
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • +1 for the complete working code and the clever use of the `typedef` and for pointing out the need for partial specialization. – CodeBricks Oct 31 '13 at 09:36