0
votes

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.

2

2 Answers

0
votes

If you can use C++11, you can do something like:

class Internal
{
    asio::deadline_timer t;

public:
    void QueueTick(const std::function<void(const boost::system::error_code&)>& handler)
    {
        t.expires_from_now(posix_time::millisec(250));
        t.async_wait(handler);
    }
};

class ForClients
{
    Internal I;
    void OnTick(const boost::system::error_code& ec) { /*...*/ }
    void Init()
    {
        I.QueueTick([this](const boost::system::error_code& ec) { OnTick(ec); });
    }
};
0
votes

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.