lancery
lancery

Reputation: 678

Variadic template function to determine function pointer return type

I have a variadic template function that takes an indirect function pointer (and its parameters) and invokes it (shown below). I was wondering how to modify this function so that it can have the proper return type matching the indirect function pointer it is invoking?

typedef UINT(APIENTRY *PFNFOO)(UINT, double);

class Foo
{
public:
    static UINT APIENTRY pfnFoo(UINT x, double y)
    {
        return (UINT)(x*y);
    }
};

template<typename PFN, typename... ARGS>
inline void CallFunc(void* pfn, const ARGS&... args)
{
    reinterpret_cast<PFN>(pfn) (args...);
}

int _tmain(int argc, _TCHAR* argv[])
{
    PFNFOO pfn = &Foo::pfnFoo;

    CallFunc<PFNFOO>(pfn, 100, 0.5f);
    return 0;
}

I tried the following but it complained that error C2782: 'RET CallFunc(RET (__cdecl *)(ARGS...),const ARGS &...)' : template parameter 'ARGS' is ambiguous. I think I am just missing the proper syntax so I would appreciate help here.

template<typename PFN, typename RET, typename... ARGS>
inline RET CallFunc(RET(*pfn)(ARGS...), const ARGS&... args)
{
    return reinterpret_cast<PFN>(pfn) (args...);
}

auto x = CallFunc<PFNFOO>(pfn, 100, 0.5f);

Upvotes: 2

Views: 394

Answers (2)

Jarod42
Jarod42

Reputation: 217293

To allow full deduction, you may use the following: (live example)

template<typename Ret, typename... Params, typename...Args>
inline Ret CallFunc(Ret(*pfn)(Params...), Args&&... args)
{
    return (*pfn)(std::forward<Args>(args)...);
}

And then call it:

CallFunc(&Foo::pfnFoo, 100, 0.5f); // float will be converted into double in CallFunc.

Upvotes: 3

Praetorian
Praetorian

Reputation: 109149

Your function expects a UINT and double as arguments, while you call it with int and float, so there is a mismatch between the types of PFNFOO's arguments, and the actual arguments you pass to CallFunc. Furthermore, there's no need to specify the function pointer type explicitly, let the compiler deduce it. Your template can be simplified to:

template<typename RET, typename... ARGS>
inline RET CallFunc(RET(*pfn)(ARGS...), ARGS&&... args)
{
    return (*pfn)(std::forward<ARGS>(args)...);
}

Usage:

CallFunc(&Foo::pfnFoo, 100U, 0.5);

And if you want to pass it an int and a float, just specify the argument types explicitly.

CallFunc<UINT, UINT, double>(&Foo::pfnFoo, 100, 0.5f);

Live demo

Upvotes: 1

Related Questions