15

The Question

I have a number of C++ functions void f(), R g(T a), S h(U a, V b) and so on. I want to write a template function that accepts f, g, h and so on as a template argument and calls that function.

ie I want something like this:

template<MagicStuff, WrappedFunction>
ReturnType wrapper(MagicallyCorrectParams... params)
{
    extra_processing(); // Extra stuff that the wrapper adds
    return WrappedFunction(params);
}
...
wrapper<f>(); // calls f
wrapper<g>(T()); // calls g
wrapper<h>(U(), V()); // calls h

Here's what I've tried so far:

Solution 1

template<typename ReturnType, typename Args...>
ReturnType wrapper(ReturnType (*wrappee)(Args...), Args... args)
{
    extra_processing();
    return wrappee(args...);
}
...
wrapper(f); // calls f OK
wrapper(g, T()); // calls g OK
wrapper(h, U(), V()); // calls h OK

This works but is unsatisfactory because in my case, I want the function pointer bound to the template instance. The function pointer is determinable statically at compile time and it is not desirable in my use case to have to pass it as a parameter at runtime.

Solution 2

template<
    typename ReturnType, typename Args...,
    ReturnType (*FuncPtr)(Args...)
>
wrapper(Args... args)
{
    extra_processing();
    return FuncPtr(args...);
}
...
wrapper<void, f>(); // calls f
wrapper<R, T, g>(T()); // calls g
wrapper<S, U, V, h>(U(), V()); // calls h

This works but is unsatisfactory because it is verbose. The return type and parameter types can be deduced from the function pointer itself. What would be perfect is a template specification so I can do wrapper<g>(T()) as indicated above.

Thanks for all help!

0xbe5077ed
  • 4,565
  • 6
  • 35
  • 77

3 Answers3

18
template<typename Fn, Fn fn, typename... Args>
typename std::result_of<Fn(Args...)>::type
wrapper(Args&&... args) {
    return fn(std::forward<Args>(args)...);
}
#define WRAPPER(FUNC) wrapper<decltype(&FUNC), &FUNC>

//Usage:

int min(int a, int b){
    return (a<b)?a:b;
}

#include<iostream>
#include<cstdlib>
int main(){
    std::cout<<WRAPPER(min)(10, 20)<<'\n';
    std::cout<<WRAPPER(rand)()<<'\n';
}

Alternatively, to get maybe quite less readable, but shorter syntax:

#define WRAPPER(FUNC, ...) wrapper<decltype(&FUNC), &FUNC>(__VA_ARGS__)

//Usage:

int main(){
    sdt::cout<<WRAPPER(min, 10, 20)<<'\n';
    std::cout<<WRAPPER(rand)<<'\n';
}
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
  • Perfect forward `args...`. And `std::result_of` can get rid of that `decltype` if you like. And dereference `fn` manually just in case it is not a function pointer (relatively pointless) – Yakk - Adam Nevraumont Aug 20 '14 at 11:53
  • @Yakk I can't use _perfect forward_, it causes errors, I already asked a [question](http://stackoverflow.com/q/25407804/3821804) about this. – GingerPlusPlus Aug 20 '14 at 14:52
  • @GingerPlusPlus thanks, I think that may be the best we can do. I'm likely going to accept it but I had a couple questions: (1) any luck compiling it under MSVC? (2) regardless, what compiler is it working on? – 0xbe5077ed Aug 20 '14 at 18:57
  • I tested it under g++, now I'll check it out under MSVC – GingerPlusPlus Aug 20 '14 at 18:59
  • @0xbe5077ed [It seems to work...](http://rextester.com/BQQVS70590) Please give me error message/tell what isn't working. – GingerPlusPlus Aug 20 '14 at 19:16
  • Internal compiler error with msvc 2015 update 1, for a bit more complex code :( – Dmitry Sazonov Mar 10 '16 at 11:14
3

This is the best I've been able to do so far:

template<typename R, typename...A>
struct S<R(A...)>
{
    typedef R(*F)(A...);
    F f;
    constexpr S(F _f) : f(_f) { }
    inline R operator()(A... a)
    { return f(a...); }
};

#define wrapper(FUNC, ARGS...) (S<decltype(FUNC)>(FUNC))(ARGS)

int f(float g);

int main(void)
{
    return wrapper(f, 3.0f);
}

Sadly I can't make it compile under MSVC.

0xbe5077ed
  • 4,565
  • 6
  • 35
  • 77
  • `ARGS...` syntax is GCC extension, use `...` instead of `ARGS...` and `__VA_ARGS__` instead of `ARGS`. Solution I recommend depends on Standard. – GingerPlusPlus Aug 20 '14 at 09:41
  • also, it don't compile under GCC anyway - line 2 causes `error: ‘S’ is not a class template` (disappear after commenting ``; and line 4 and 7 cause `error: function returning a function`, I don't know how to fix it. – GingerPlusPlus Aug 20 '14 at 10:12
  • 2
    [It compiles fine](http://rextester.com/BFXXZ39586) if you: add `S` class template declaration (your code defines only its specialization), remove `constexpr` (MS VC doesn't support it yet), add body to `f` function (to avoid linker errors). Other changes are not so significant. – Constructor Aug 20 '14 at 10:37
3

There's a duplicate somewhere here, I remember it, but I can't find it... The conclusion of which being that it was impossible to pass the pointer's type and its value at the same time.

Some hope lies in a suggestion for implicit type template parameters, which you can find here.

Quentin
  • 62,093
  • 7
  • 131
  • 191