I'm not sure how else to name this entry, it's rather complicated. So what I was finally able to achieve is to pass a boost::bind
to a boost::bind
with placeholders, with templated callbacks. I'm not gonna lie, this took me awhile to figure out.
The rationale for all of this was that I had a parent class called group
, and this group had the list of boost::asio::deadline_timer
's and the boost::asio::io_service
running in a thread. I wanted the ability for child objects to put work in that thread.
class group
{
public:
...
typedef std::shared_ptr< boost::asio::deadline_timer > deadline_timer_pointer;
typedef std::vector< deadline_timer_pointer > deadline_timer_collection;
private:
...
boost::signals2::mutex _timers_mutex;
deadline_timer_collection _timers;
};
But I also wanted everything to be robust and not leak, so I wanted group
to manage all the timers and remove them after they fired.
Here's the external interface for callers.
class group
{
public:
...
// public to allow external objects to put something in our timer queue
// don't wait on returned timer, call group::async_wait so that timer is automatically removed from internal list
deadline_timer_pointer make_timer( int period_ms );
template<typename CB>
void async_wait( deadline_timer_pointer timer, CB callback );
};
And here's an example of how to use the api.
void child_object::my_callback( const boost::system::error_code& ec, int n )
{
...
}
{ // elsewhere
group::deadline_timer_pointer timer = _group->make_timer( 0 );
auto callback = boost::bind( &child_object::my_callback, this, _1, 100 );
_group->async_wait( timer, callback );
}
And now the magic. The hardest part was figuring out that I needed the two layers so that I could boost::protect
the callback. Realizing I also needed to template (the CB in &group::cb_async_wait<CB>
) the bind wasn't obvious from the 300 lines of template errors.
template<typename CB>
void group::async_wait( deadline_timer_pointer timer, CB callback )
{
// the template itself must be protected, hence 2 layers
// don't make the caller call boost::protect, hence 2 layers
_async_wait( timer, boost::protect( callback ) );
}
// put our own callback first so that we can remove the timer
template<typename CB>
void group::_async_wait( deadline_timer_pointer timer, CB callback )
{
auto our_callback = boost::bind( &group::cb_async_wait<CB>, this, _1, timer, callback );
timer->async_wait( our_callback );
}
// after removing the timer call the real callback
template<typename CB>
void group::cb_async_wait( const boost::system::error_code& ec, deadline_timer_pointer timer, CB callback )
{
del_timer( timer );
callback( ec ); // other parameters in callback() have already been populated through boost magic
}