Оберните указатель функции в С++ с вариационным шаблоном

Вопрос

У меня есть ряд функций С++ void f(), R g(T a), S h(U a, V b) и так далее. Я хочу написать функцию шаблона, которая принимает f, g, h и т.д. Как аргумент шаблона и вызывает эту функцию.

т.е. я хочу что-то вроде этого:

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

Вот что я пробовал до сих пор:

Решение 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

Это работает, но неудовлетворительно, потому что в моем случае я хочу, чтобы указатель функции привязывался к экземпляру шаблона. Указатель функции определяется статически во время компиляции, и в моем случае нежелательно передавать его в качестве параметра во время выполнения.

Решение 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

Это работает, но неудовлетворительно, потому что оно многословно. Тип возвращаемого значения и типы параметров могут быть выведены из самого указателя функции. То, что было бы идеально, это спецификация шаблона, поэтому я могу сделать wrapper<g>(T()), как указано выше.

Спасибо за помощь!

Ответ 1

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>

//Использование:

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';
}

В качестве альтернативы можно получить, возможно, менее читаемый, но более короткий синтаксис:

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

//Использование:

int main(){
    sdt::cout<<WRAPPER(min, 10, 20)<<'\n';
    std::cout<<WRAPPER(rand)<<'\n';
}

Ответ 2

Это лучшее, что я смог сделать до сих пор:

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);
}

К сожалению, я не могу скомпилировать его под MSVC.

Ответ 3

Там где-то здесь есть дубликат, я его помню, но я не могу его найти... Заключение в том, что невозможно одновременно передавать тип указателя и его значение.

Некоторая надежда заключается в предположении для неявных параметров шаблона типа, которые вы можете найти здесь.

Ответ 4

Чтобы добавить к прохладному ответу GingerPlusPlus, с помощью C++ 14 вы можете использовать decltype (auto) для определения типа возврата, избавляясь от необходимости в макросе. Это работает для меня:

template <typename Fn, Fn fn, typename... Args>
decltype(auto) wrapper(Args&&... args) {
    return fn(std::forward<Args>(args)...);
}