Фактическая подпись обработчика async_wait ()

У меня есть двухуровневая структура объекта, в которой содержащийся объект имеет deadline_timer, а внешний объект имеет функцию обработчика, например:

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));
    }
};

Вызов QueueTick() не компилируется в MSVS 2008 с «невозможно преобразовать параметр 1 из 'boost :: _ bi :: bind_t' в 'void (__cdecl *) (const boost :: system :: error_code &)'».

Если я сделаю элемент таймера общедоступным и сделаю прямой вызов I.t.async_wait() с тем же аргументом, он будет успешным. Ясно, что подпись обработчика более особенная, чем та, что я использовал в объявлении QueueTick; однако я не могу найти символ, который его определяет, и я не знаю, как интерпретировать метапрограммирование, происходящее внутри шаблона basic_deadline_timer<>.


person Mike C    schedule 01.06.2012    source источник


Ответы (2)


async_wait таймера asio можно вызвать с любым вызываемым типом, который можно вызвать с аргументом boost::system::error_code const&. Нигде нет единственного типа, который определял бы его, просто он должен быть вызван с задокументированным типом аргумента.

Тип вашего параметра QueueTick - это один из таких вызываемых типов, указатель на простую функцию, не являющуюся членом, с правильной подписью:

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

Но результатом boost::bind является тип класса с перегруженным operator(), который не может быть преобразован в этот тип указателя функции.

Есть несколько способов решить эту проблему, но самый простой, вероятно, - это следовать самому async_wait и написать QueueTick в качестве шаблона функции, принимая любой тип:

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);
    }
};

Остальной код останется без изменений.

Если это не вариант (например, потому что QueueTick должен быть виртуальным), вы можете использовать boost::function, где может храниться любой вызываемый объект с правильной подписью:

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);
    }
};

Это будет иметь небольшие накладные расходы по сравнению с версией шаблона из-за создания объекта boost::function.

person Jonathan Wakely    schedule 02.06.2012
comment
Спасибо, boost :: function - это то, что я искал. - person Mike C; 05.06.2012
comment
Работая с этим, кажется, что Boost.ASIO должен делать то же самое - использовать подпись функции ‹› вместо шаблонного метода async_wait (). Соглашаться? - person Mike C; 05.09.2012
comment
Что значит делать? Если вы говорите, что ASIO было бы лучше, если бы это было так, я не согласен. Нет никаких преимуществ в добавлении зависимости от function<>, шаблон функции-члена отлично работает для любого вызываемого типа, без необходимости выделять память или зависеть от доступности function<>. - person Jonathan Wakely; 10.09.2012

Если вы умеете использовать C ++ 11, вы можете сделать что-то вроде:

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); });
    }
};
person betabandido    schedule 01.06.2012