问题 使用可变参数模板在C ++中包装函数指针


问题

我有很多C ++函数 void f()R g(T a)S h(U a, V b) 等等。我想写一个接受的模板函数 fgh 等等 模板参数 并调用该函数。

 我想要这样的东西:

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()) 如上所述。

谢谢大家的帮助!


3174
2017-08-19 21:04


起源

那怎么样? std::function<> ? - quantdev
@quantdev,我试图避免使用 std::function 解决问题。如果可能的话,我希望我的包装器<f>实例化一个函数,该函数的地址本身在编译时是可以确定的。 - 0xbe5077ed
你也可以这样做 wrapper<decltype(f)>()。 - 0x499602D2
你想要的东西 template <typename T, T Val> struct Value;,和“演绎” T。据我所知,目前这是不可能的,虽然提出某种建议 auto T 不断上升。 - Kerrek SB
为了回应@KerrekSB,我认为可以编写一个用作的包装器 wrapper<decltype(f), f>();,但没有一个用作 wrapper<f>(); - aschepler


答案:


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

9
2017-08-20 11:45



完美的前进 args...。和 std::result_of 可以摆脱它 decltype 如果你喜欢。和解除引用 fn 手动以防万一它不是一个函数指针(相对没有意义) - Yakk - Adam Nevraumont
@Yakk我不能用 完美的前进,它导致错误,我已经问过了 题 对这个。 - GingerPlusPlus
@GingerPlusPlus谢谢,我认为这可能是我们能做的最好的。我可能会接受它,但我有几个问题:(1)根据MSVC编译它的运气好吗? (2)无论如何,它正在编译什么编译器? - 0xbe5077ed
我在g ++下测试它,现在我将在MSVC下检查它 - GingerPlusPlus
@ 0xbe5077ed,感谢有趣的问题:D - GingerPlusPlus


这里有一个副本,我记得它,但是我找不到它......结论是不可能同时传递指针的类型及其值。

一些希望在于对隐式类型模板参数的建议,您可以找到它们 这里


3
2017-08-20 15:06





这是迄今为止我能做到的最好的:

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下编译。


2
2017-08-19 22:11



ARGS... 语法是GCC扩展,使用 ... 代替 ARGS... 和 __VA_ARGS__ 代替 ARGS。我建议的解决方案取决于标准。 - GingerPlusPlus
另外,它不会在GCC下编译 - 第2行导致 error: ‘S’ is not a class template (评论后消失 <R(A...)>;和第4和第7行的原因 error: function returning a function,我不知道如何解决它。 - GingerPlusPlus
它汇编很好如果你:添加 S 类模板声明(您的代码仅定义其特化),删除 constexpr (MS VC还不支持它),添加body f 函数(以避免链接器错误)。其他变化并不那么重要。 - Constructor
对于使用预处理器的想法+1,它激发了我的灵感。 - GingerPlusPlus