Search code examples
c++boostboost-asioboost-bind

Actual signature of async_wait() handler


I have a two-layer object structure where the contained object has a deadline_timer and the outer object has the handler function, as:

class Internal
{
    asio::deadline_timer t;
public:
    void QueueTick(void (*handler)(boost::system::error_code const&))
    {
       t.expires_from_now(posix_time::millisec(250));
       t.async_wait(handler);
    }
};

class ForClients
{
    Internal I;
    void OnTick(boost::system::error_code const&) { /*...*/ }
    void Init()
    {
        I.QueueTick(boost::bind(&cdevXcite::OnTick, this, _1));
    }
};

The QueueTick() call is failing to compile in MSVS 2008 with "cannot convert parameter 1 from 'boost::_bi::bind_t' to 'void (__cdecl *)(const boost::system::error_code &)'".

If I make the timer member public and make a direct call to I.t.async_wait() with the same argument, it succeeds. Clearly, the handler's signature is more special than what I've used in the QueueTick declaration; however, I can't find a symbol that defines it and I don't know how to interpret the metaprogramming going on inside the basic_deadline_timer<> template.


Solution

  • An asio timer's async_wait can be called with any callable type that can be called with a boost::system::error_code const& argument. There isn't a single type anywhere that defines it, it just has to be callable with the documented argument type.

    The type of your QueueTick parameter is one such callable type, a pointer to a plain ol' non-member function with the right signature:

    void QueueTick(void (*handler)(boost::system::error_code const&))
    

    But the result of boost::bind is a class type with an overloaded operator() which is not convertible to that function pointer type.

    There are a few ways to solve this, but the simplest is probably to follow async_wait itself and write QueueTick as a function template, accepting any type:

    class Internal
    {
      asio::deadline_timer t;
    public:
      template<WaitHandle>
        void QueueTick(WaitHandle handler)
        {
          t.expires_from_now(posix_time::millisec(250));
          t.async_wait(handler);
        }
    };
    

    The rest of the code would be unchanged.

    If that's not an option (e.g. because QueueTick needs to be virtual) then you could use boost::function with can hold any callable object of the right signature:

    class Internal
    {
      asio::deadline_timer t;
    public:
      typedef boost::function<void(boost::system::error_code const&)> handler_type;
      void QueueTick(handler_type handler)
        {
          t.expires_from_now(posix_time::millisec(250));
          t.async_wait(handler);
        }
    };
    

    This will have a small overhead compared to the template version, due to constructing the boost::function object.