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);
}
}
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);
}
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.
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{ ... };
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?
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:By partially specializing
std::is_placeholder
for the above template,std::bind
sees the instantiationsplaceholder_template<N>
as placeholder types. With the usual indices trick, we then expandplaceholder_template<0>{}, placeholder<1>{}, ...., placeholder<N-1>{}
, whereN
is the number of function parameters.The code for the placeholder generator and the integer sequence, from the other answer:
Side remark: If you want to accept member functions with cv- and ref-qualifiers, you could use a very general "pattern" like
and restrict via SFINAE. Here's a trait that lets you deduce the number of parameters from such a function pointer (via
tuple_size
).