绑定通用成员函数 [英] Binding a generic member function
问题描述
有时我需要绑定一些成员函数到它的调用对象,以同样的方式处理成员函数和非成员函数。例如(特征回调示例):
Sometimes I need to bind some member functions to its calling object, to treat member functions and non-member functions in the same homogeneous way. For example (The tipical callback example):
#include <vector>
#include <functional>
void f(int){}
struct foo
{
void f(int){}
};
int main()
{
using namespace std::placeholders;
foo my_foo;
std::vector<std::function<void()>> functions;
functions.push_back( f );
functions.push_back([](int){});
functions.push_back( std::bind( &foo::f , my_foo , _1 ) );
for( auto& function : functions )
{
function(0);
}
}
成员函数具有更多的参数,需要放在 std :: bind()
调用的内部。
As more parameters the member function has, more placeholders we need to put inside the std::bind()
call.
现在考虑一个通用版本。不应该有问题,不是?:
Now consider a generic version of that. There shouldn't be problem, isn't?:
#include <vector>
#include <functional>
void f(int){}
struct foo
{
void f(int){}
};
template<typename FIRST , typename SECOND , typename THIRD>
class callback_list
{
using callback_t = std::function<void(FIRST,SECOND,THIRD>)>;
//Overload for non-member handlers:
void add( const callback_t& handler )
{
_handlers.push_back( handler );
}
//Overload for member handlers:
template<typename CLASS>
void add( CLASS& object_ref ,
void(CLASS::*member_function)( FIRST,SECOND,THIRD ) )
{
using namespace std::placeholders;
_handlers.push_back( std::bind( member_function ,
std::ref( object_ref ) ,
_1 , _2 , _3
)
);
}
template<typename... ARGS>
void operator()( ARGS&&... args )
{
for( auto& handler : _handlers )
handler( std::forward<ARGS>( args )... );
}
private:
std::vector<callback_t> functions;
};
void f(int,int,int){}
struct foo
{
void f(int,int,int){}
};
int main()
{
using namespace std::placeholders;
foo my_foo;
callback_list<int,int,int> callbacks;
callbacks.add( f );
callbacks.add([](int,int,int){});
callbacks.add( my_foo , &foo::f );
callbacks(0,0,0);
}
好的。成员回调的 add()
重载只是将对象绑定到成员函数,并且因为回调有三个参数,我们使用三个占位符。
Ok. The add()
overload for member callbacks just binds the object to the member function, and because the callbacks are of three parameters, we use three placeholders.
但是请考虑这样:如果回调有任意数量的参数会怎样?。
换句话说,如果 callback_list
类模板是用可变参数模板定义的:
But consider this: What if the callbacks have any number of parameters?.
In other words, what I have to do if the callback_list
class template is defined with a variadic template?:
template<typename... ARGS>
class callback_list{ ... };
如何将变量函数与任何已知的函数参数绑定 std :: bind()
调用,即,具有未指定数量的占位符?
How can I bind a variadic function with any function parameter known at the point of the std::bind()
call, i.e., with an unspecified number of placeholders?
推荐答案
要使用 std :: bind
提供一定数量的占位符,具体取决于回调函数的参数数量。我在这里介绍了如何通过为占位符创建生成器的方法:
To work with std::bind
, we need to somehow supply a certain amount of placeholders, depending on the amount of function parameters of the callback. I've described a way here how you can do that, by creating a generator for placeholders:
template<int> struct placeholder_template {};
通过部分专门化 std :: is_placeholder
上述模板 std :: bind
将实例化 placeholder_template< N>
视为占位符类型。使用通常的索引技巧,然后将 placeholder_template< 0> {},占位符< 1> {},...,占位符< N-1> {}
N
是函数参数的数量。
By partially specializing std::is_placeholder
for the above template, std::bind
sees the instantiations placeholder_template<N>
as placeholder types. With the usual indices trick, we then expand placeholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{}
, where N
is the number of function parameters.
template<typename... Params>
class callback_list
{
public:
using callback_t = std::function<void(Params...)>;
//Overload for non-member handlers:
void add( const callback_t& handler )
{
_handlers.push_back( handler );
}
private:
//Overload for member handlers:
template<typename CLASS, int... Is>
void add( CLASS& object_ref ,
void(CLASS::*member_function)( Params... ) ,
int_sequence<Is...> )
{
using namespace std::placeholders;
_handlers.push_back( std::bind( member_function ,
std::ref( object_ref ) ,
placeholder_template<Is>{}...
)
);
}
public:
template<typename CLASS>
void add( CLASS& object_ref ,
void(CLASS::*member_function)( Params... ) )
{
add( object_ref, member_function,
make_int_sequence<sizeof...(Params)>{} );
}
template<typename... ARGS>
void operator()( ARGS&&... args )
{
for( auto& handler : _handlers )
handler( std::forward<ARGS>( args )... );
}
private:
std::vector<callback_t> _handlers;
};
占位符生成器的代码和整数序列,来自其他答案:
The code for the placeholder generator and the integer sequence, from the other answer:
template<int...> struct int_sequence {};
template<int N, int... Is> struct make_int_sequence
: make_int_sequence<N-1, N-1, Is...> {};
template<int... Is> struct make_int_sequence<0, Is...>
: int_sequence<Is...> {};
template<int> // begin with 0 here!
struct placeholder_template
{};
#include <functional>
#include <type_traits>
namespace std
{
template<int N>
struct is_placeholder< placeholder_template<N> >
: integral_constant<int, N+1> // the one is important
{};
}
想要接受具有cv-和ref-qualifiers的成员函数,可以使用非常一般的模式,如
Side remark: If you want to accept member functions with cv- and ref-qualifiers, you could use a very general "pattern" like
template<class C, class T>
void add(C& ref, T fun);
并通过SFINAE进行限制。 这里有一个特征,可让您从这样的函数指针中推导出参数的数量(通过 tuple_size
)。
and restrict via SFINAE. Here's a trait that lets you deduce the number of parameters from such a function pointer (via tuple_size
).
这篇关于绑定通用成员函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!