Boost绑定占位符参数等于可变模板参数的数量

Boost bind placeholder argument equal to the number of Variadic Template arguments

本文关键字:参数 于可变 绑定 占位符 Boost      更新时间:2023-10-16

我想知道是否有可能在boost::bind调用中使用传递给可变参数模板的参数数量作为占位符。

像这样:

template <typename ... Args>
boost::bind(&function, this, anArg, _1));         //If Args count equals 1
boost::bind(&function, this, anArg, _1, _2));     //If Args count equals 2
boost::bind(&function, this, anArg, _1, _2, _3)); //If Args count equals 3

这可能吗?

谢谢

一定有部分专门化的方法。你的变量不能马上知道参数的个数,对吧?您必须使用编译时递归,在此期间,您可以使用boost::mpl堆叠参数(或使用简单的整型常量增量计数)。然后在你的最后一个非可变递归调用(带0参数)你调用mpl::size在你的容器(或者只是使用整数计数器,如果你选择那种方式)调用Callable像其他答案,它承担所有的参数,加上一个整型模板参数在类型列表的开始。这就是你的专长。您为每个数量的参数创建一个调用者,该调用者将根据其特定数量的参数调用正确的绑定。可调用结构(部分)根据实参的数量进行专门化。即使Call函数接受最大数量的参数,它也只包装正确的boost::bind调用(例如,callable2, T1, T2, T3>)的bind(..,_1,_2)这并不可怕,但我确认我过去在c++ 03中使用过这种方法。

也许你应该更详细地解释一下你想做什么。如果你只是在寻找一个解决方案来处理三个不同的签名,它们的参数类型不同,你可以这样做:

template<typename signature>
struct callable;
template<typename P0, typename P1, typename P2>
struct callable<void (P0, P1, P2)>
{
    void bind()
    {
        boost::bind(&callable::operator(), this, _1, _2, _3);
    }
    void operator()(P0, P1, P2) {}
};

这不是针对特定问题的答案,而是针对您可能试图解决的问题的一个很好的变通方法。

我在实现通用委托机制时遇到了同样的问题。我的解决方案是在bind调用之上使用一个包装器,将其专门用于变体。虽然它没有解决问题,但它绝对减少了绑定调用的冗余代码,最重要的是,它给了我一个基于可变参数的委托系统,我可以在任何地方使用。

template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(), CALLBACK_TARGET_CLASS* callbackTarget)
{
    return std::bind(memberFunction, callbackTarget);
}
template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0), CALLBACK_TARGET_CLASS* callbackTarget)
{
    return std::bind(memberFunction, callbackTarget, std::placeholders::_1);
}
template<class CALLBACK_TARGET_CLASS, typename RETURN_TYPE, typename P0, typename P1>
std::function<RETURN_TYPE()> BindFunction(RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(P0, P1), CALLBACK_TARGET_CLASS* callbackTarget)
{
    return std::bind(memberFunction, callbackTarget, std::placeholders::_1, std::placeholders::_2);
}

template<typename RETURNTYPE, typename... ARGS>
struct Delegate
{
    std::function<RETURN_TYPE (ARGS...)> callbackFunction;
    template<class CALLBACK_TARGET_CLASS>
    void Bind(CALLBACK_TARGET_CLASS* callbackTarget, RETURN_TYPE (CALLBACK_TARGET_CLASS::*memberFunction)(ARGS...))
    {
        callbackFunction = BindFunction<CALLBACK_TARGET_CLASS, RETURN_TYPE, ARGS...>(memberFunction, callbackTarget); 
    }
    void Callback(ARGS... params)
    {
        callbackFunction(params...);
    }
};

用法结束我们看起来像这样…

class Foo
{
public:
    void Bar(int x);
}
Foo foo;
Delegate<void, int> myDelegate;
myDelegate.Bind(&foo, &Foo::Bar);
myDelegate.Callback(3);

使用_1,_2,…直接使用可变模板是不可能的。您需要使用扩展宏。

但是,您可以将这些占位符包装在模板工厂中,以获得带有模板参数1的_1,用于2的_2,等等…

实现如gcc/msvc已经将占位符定义为模板结构(分别为std::_Placeholder和std::_Ph),所以你可以这样定义你的工厂:

struct ph_factory {
    template<size_t holder>
    static std::_Placeholder<holder> make_ph() {
        return std::_Placeholder<holder>();
    }
};

定义后,您可以使用所需的所有占位符展开参数包:

struct tester {
    template<size_t ... holders>
    void test(int val) {
        auto callable = std::bind(&tester::call, this, val, ph_factory::make_ph<holders>()...);
        callable('a', 42, 'c');
    }
    void call(int v1, char c1, int v2, char c2) {
        cout << "calling :" << v1 << " " << c1 << " " << v2 << " " << c2 << endl;
    }
};

所以下面的代码将输出"calling:10 c 42 a"

int main() {
    tester t;
    t.test<3,2,1>(10);
}

使用像make_index这样的技巧将使您有可能实现最初的目标。