@ wrote... (5 years, 10 months ago)

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 rational 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
}
Category: tech, Tags: boost, cpp, templates
Comments: 0
Click here to add a comment