Search code examples
c++templatesfunction-pointers

Assignen a template function with Variadic arguments to a function pointer


In C++ I am trying to make forwarding wrapper that takes the first argument and calls a method on it. This ended up in the wrapper method in the given code. This works fine when calling it.

However I want to assign this templated wrapper to function pointer. In my use-case the function pointer is a given and I can not change it to a std::function or something like that. This is because the place where it is used in a C api.

I did create the following example:

#include <iostream>

template<auto FUNC, typename T, typename ... Params>
static auto wrapper(void* handle, Params&& ... args) {
    auto* ser = static_cast<T*>(handle);
    return (ser->*FUNC)(std::forward<Params>(args)...);
}

class MyTestClass {
public:
    int method1(int i, int j) { return i + j; }

    float method2(float f) {
        return f * 2;
    }
};

int main() {
    MyTestClass thing{};

    int (*serialize)(void* handle, int i, int j);

    serialize = wrapper<&MyTestClass::method1, MyTestClass>;
    
    auto callMethod1Result = wrapper<&MyTestClass::method1, MyTestClass>(&thing, 1, 2);
    std::cout << "callMethod1Result: " << callMethod1Result << std::endl;

    return 0;
}

The calling of the method works fine, however:

    int (*serialize)(void* handle, int i, int j);
    serialize = wrapper<&MyTestClass::method1, MyTestClass>;

does not work, giving me the error:

/.../temp.cpp:23:17: error: no matches converting function ‘wrapper’ to type ‘int (*)(void*, int, int)’
     serialize = wrapper<&MyTestClass::method1, MyTestClass>;
                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/.../temp.cpp:4:13: note: candidate is: ‘template<auto FUNC, class T, class ... Params> auto wrapper(void*, Params&& ...)’
 static auto wrapper(void* handle, Params&& ... args) {
             ^~~~~~~

after some trying I did find out that the Params&& ... args part is causing the issue, because if I make a more explicit wrapper without variadic argument, then it does work.

My main question is: Can I assign a templated method with variadic argument to a function pointer, and how?


Solution

  • The problem is wrapper taking args as forwarding reference, its type would always be reference: lvalue-reference or rvalue-reference. But serialize is declared as function pointer taking i and j by-value with type int, they can't match reference type, which makes template argument deduction on Params fails in serialize = wrapper<&MyTestClass::method1, MyTestClass>;.

    You can fix it by changing type of i and j in declaration of serialize.

    E.g.

    int (*serialize)(void* handle, int&& i, int&& j);
    serialize = wrapper<&MyTestClass::method1, MyTestClass>;
    

    LIVE Clang LIVE Gcc

    Or change wrapper taking args by-value.

    E.g.

    template<auto FUNC, typename T, typename ... Params>
    static auto wrapper(void* handle, Params ... args)
    

    LIVE Clang LIVE Gcc