可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I want to know if it is possible to use the number of arguments passed to a variadic template as placeholder in a boost::bind call.
Something like this:
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
Is this possible?
Thank you
回答1:
There definitely is a way with partial specialization.
your variadic doesn't know the number of arguments right away right ? you have to use compile-time recursion, during this time you can stack your arguments using boost::mpl (or count them using a simple integral constant increment).
then in your last non-variadic recursion call (with 0 arg) you call mpl::size on your container (or just use the integral counter if you chose that way) to call a Callable like the other answer, that bears all the arguments, plus one integral template paramater at the beginning of the type list. and that is what you specialize. you make a caller for each number of arguments that will call the correct bind according to its specialized number of arguments.
(the Callable structures are (partially) specialized according to the number of argument integral template parameter. and even though the Call function takes the max number of argument, it only wraps the correct boost::bind call for example the bind(..,_1,_2) for the Callable<2, T1, T2, T3>)
its not terrible, but I confirm that I have used this approach in C++03 in the past.
回答2:
Maybe you should explain what you want to do in a little bit more detail. If you're just looking for a solution to handle three different signatures which differ by their parameter types, you could do something like that:
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) {}
};
回答3:
This is not an answer to the specific problem, but a nice workaround for the problem you are likely trying to solve.
I ran into the same issue when implementing a generic delegate mechanism. My solution was to use a wrapper on top of just the bind call, specializing that for the variations. While it does not solve the issue, it definitely minimizes the redundant code to just the bind call and most importantly gives me a variadic parameter based delegate system I can use everywhere.
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...);
}
};
Usage ends us looking like this..
class Foo
{
public:
void Bar(int x);
}
Foo foo;
Delegate<void, int> myDelegate;
myDelegate.Bind(&foo, &Foo::Bar);
myDelegate.Callback(3);
回答4:
Using _1, _2, ... directly is not possible with variadic template. You need to use expansive macros instead.
However, you can wrap theses placeholders in a templated factory to get _1 with the template argument 1, _2 for 2, etc ...
Implementations such as gcc / msvc already define placeholders as templated struct (respectively std::_Placeholder and std::_Ph) so you can define you factory this way:
struct ph_factory {
template<size_t holder>
static std::_Placeholder<holder> make_ph() {
return std::_Placeholder<holder>();
}
};
This defined, you can expand a parameter pack with all the placeholders you want :
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;
}
};
So the following code will output "calling:10 c 42 a"
int main() {
tester t;
t.test<3,2,1>(10);
}
Using tricks like make_indice will grant you the possibility to achieve you original goal.