boost::bind с функциями-членами (как обработчик асинхронной записи boost::asio)

Я использую boost:: bind для передачи функции-обработчика boost::asio::async_write. Когда я использую бесплатные функции, все работает нормально, но когда я пытаюсь переместить функции в класс, связывание приводит к ошибкам, которые я не могу расшифровать.

Что работает:

Я пишу некоторые данные с:

boost::asio::async_write(*socket,
                         boost::asio::buffer(data(),
                                             length()),
                         boost::bind(handlermessagewrite,
                                     boost::asio::placeholders::error,
                                     this,
                                     boost::asio::placeholders::bytes_transferred));

Затем я обрабатываю запись свободной функцией, подпись которой:

void handlermessagewrite(const boost::system::error_code& errorcode,
                         iodata *msg,
                         size_t bytes_transferred);

Это все работает, как ожидалось.

Что я пытаюсь сделать:

Я перемещаю обработчик внутри класса ioclient:

class ioclient {
public:
  void handlermessagewrite(const boost::system::error_code& errorcode,
                           iodata *msg,
                           size_t bytes_transferred);
}

void ioclient::handlermessagewrite(const boost::system::error_code& errorcode,
                                   iodata *msg,
                                   size_t bytes_transferred);

и соответственно адаптируем код boost:: bind, как видно из официальных руководств asio:

- boost::bind(handlermessagewrite,

+ boost::bind(&ioclient::handlermessagewrite,

Тем не менее, это приводит к очень непрозрачным ошибкам компиляции, чему не помогает тот факт, что одна из строк в моей IDE обрезается (code:: blocks):

\ Подталкивание \ привязывать \bind_template.hpp|102| требуется от 'boost::_bi::bind_t::result_type boost::_bi::bind_t::operator()(const A1&, const A2&) [с A1 = boost::system::error_code; A2 = без знака int; R = пустота; F = повышение::_mfi::mf2; L = boost::_bi::list3 (*)(), boost::_bi::value, boost::arg<2> (*)()>; boost::_bi::bind_t::result_type = void]'| \ Подталкивание \ ASIO \ осущ \write.hpp|261| требуется от 'void boost::asio::detail::write_op::operator()(const boost::system::error_code&, std::size_t, int) [with AsyncWriteStream = boost::asio::basic_stream_socket; CompletionCondition = boost::asio::detail:: Transfer_all_t; WriteHandler = boost::_bi::bind_t, boost::_bi::list3 (*)(), boost::_bi::va| \ Подталкивание \ ASIO \ осущ \write.hpp|585| требуется от 'void boost::asio::async_write(AsyncWriteStream&, const ConstBufferSequence&, WriteHandler&&) [with AsyncWriteStream = boost::asio::basic_stream_socket; ConstBufferSequence = boost::asio::mutable_buffers_1; WriteHandler = boost::_bi::bind_t, boost::_bi::list3 (*)(), boost::_bi::value, boost::arg<2> (*)()> >]'| \ Iodata.cpp|76| требуется отсюда | \boost\bind\bind.hpp|392| ошибка: нет совпадения для вызова '(boost::_mfi::mf2) (const boost::system::error_code&, iodata*&, const unsigned int&)'| \boost\bind\mem_fn_template.hpp|253| Примечание: кандидаты:| \boost\bind\mem_fn_template.hpp|278| примечание: R boost::_mfi::mf2::operator()(T*, A1, A2) const [с R = void; Т = иоклиент; A1 = const boost::system::error_code&; A2 = йодата *]| \boost\bind\mem_fn_template.hpp|278| примечание: неизвестное преобразование для аргумента 1 из 'const boost::system::error_code' в 'ioclient*'| \boost\bind\mem_fn_template.hpp|283| примечание: шаблон R boost::_mfi::mf2::operator()(U&, A1, A2) const [с U = U; R = пустота; Т = иоклиент; A1 = const boost::system::error_code&; A2 = йодата *]| \boost\bind\mem_fn_template.hpp|283| примечание: не удалось вывести / заменить аргумент шаблона:| \boost\bind\bind.hpp|392| примечание: невозможно преобразовать '(& a)->boost::_bi::list2::operator[]((* &((boost::_bi::list3 (*)) (), boost:: _ bi:: value, boost:: arg<2> () ()>) this) -> boost:: _ bi:: list3 (*) (), boost:: _ bi:: value, boost:: arg<2> (*) ()>::. boost:: _ bi:: storage3 (*) (), boost:: _ bi:: value, boost:: arg<2> (*) ()>::.boost:: _ bi:: storage2 (*) (), boost::bi:: value>:: a2)) '(введите'iodata*') для ввода'const boost::system::| \boost\bind\mem_fn_template.hpp|291|note: шаблон R boost::_mfi::mf2::operator()(const U&, A1, A2) const [с U = U; R = пустота; Т = иоклиент; A1 = const boost::system::error_code&; A2 = йодата *]| \boost\bind\mem_fn_template.hpp|291| примечание: не удалось вывести / заменить аргумент шаблона:| \boost\bind\bind.hpp|392| примечание: невозможно преобразовать '(& a)->boost::_bi::list2::operator[]((* &((boost::_bi::list3 (*)) (), boost:: _ bi:: value, boost:: arg<2> () ()>) this) -> boost:: _ bi:: list3 (*) (), boost:: _ bi:: value, boost:: arg<2> (*) ()>::. boost:: _ bi:: storage3 (*) (), boost:: _ bi:: value, boost:: arg<2> (*) ()>::.boost:: _ bi:: storage2 (*) (), boost::bi:: value>:: a2)) '(введите'iodata*') для ввода'const boost::system::| \boost\bind\mem_fn_template.hpp|299| примечание: R boost::_mfi::mf2::operator()(T&, A1, A2) const [с R = void; Т = иоклиент; A1 = const boost::system::error_code&; A2 = йодата *]| \boost\bind\mem_fn_template.hpp|299| примечание: неизвестное преобразование для аргумента 1 из 'const boost::system::error_code' в 'ioclient&'|

Я убежден, что я делаю что-то не так с bind, но я не знаю, что это может быть. Есть идеи?

2 ответа

При использовании метода экземпляра вы должны передать this указатель в качестве второго аргумента для bind().

РЕДАКТИРОВАТЬ:

Я сделал то, что вы пытаетесь сделать с boost::asio. Вот фрагмент из моей реализации:

        boost::asio::async_write(
            m_Socket,
            boost::asio::buffer((const unsigned char *)(rMsg.c_str()), rMsg.length()),
            boost::bind(&ServerToClientConnT::HandleAsioWrite,
                        this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));

где HandleAsioWrite - метод, объявленный следующим образом:

void HandleAsioWrite(const boost::system::error_code& rErrorCode,
                     std::size_t nBytesTransferred);

Исходный пример терпит неудачу, потому что экземпляр объекта, для которого будет вызываться функция-член, не передается bind, Это указывается в ошибке компилятора, где говорится, что нет известного преобразования из const boost::system::error_code в ioclient*,

При использовании Boost.Bind с указателями на членов, документально подтверждено, что

boost::bind(&X::f, args) эквивалентно boost::bind<R>(boost::mem_fn(&X::f), args),

Кроме того, документация Boost.mem_fn гласит:

Это [ boost::mem_fn ] поддерживает указатели на функции-члены с более чем одним аргументом, и возвращаемый объект функции может принимать указатель, ссылку или умный указатель на экземпляр объекта в качестве первого аргумента.

Таким образом, если этот первый аргумент boost::bind это указатель на член, то либо:

  • Второй аргумент в вызове bind должен быть дескриптором экземпляра объекта, так как он будет первым аргументом, переданным объекту функции, возвращенному из boost::mem_fn,
  • Объект функции вернулся из boost::bind должен быть передан дескриптор экземпляра объекта в позиции аргумента, соответствующей _1 заполнитель.

Например, учитывая

struct Foo
{
  void do_something(int x) {}
};

do_something Функция-член может быть связана и вызываться с любым из следующих действий:

Foo f;
boost::bind(&Foo::do_something, &f, _1)(42);     // handle is second argument.
boost::bind(&Foo::do_something, _1, _2)(&f, 42); // handle matches _1 position.
boost::bind(&Foo::do_something, _2, _1)(42, &f); // handle matches _1 position.

С Boost.Asio, boost::asio::placeholders::error реализован как заполнитель _1, По этой причине экземпляр объекта должен быть передан bind Вызовите в качестве второго аргумента, или экземпляр объекта должен быть передан в качестве аргумента либо свободной функции, либо статической функции-члену, которая затем вызовет функцию-член для экземпляра объекта. Вот пример решения, которое компилируется с нестатической функцией-членом, но фрагмент интересов:

ioclient client;
iodata data;

boost::asio::async_write(
  socket,
  boost::asio::null_buffers(),
  boost::bind(&ioclient::handlermessagewrite,
              &client,
              boost::asio::placeholders::error,
              &data,
              boost::asio::placeholders::bytes_transferred));

Ошибка компилятора, опубликованная в ответе Lou, указывает на то, что ioclient функция-член пытается быть вызвана с дескриптором экземпляра iodata несовместимый тип. За iodata чтобы быть совместимым типом, он должен быть унаследован от ioclient, Если это предполагаемая иерархия типов, проверьте правильность наследования. В противном случае тщательно сопоставьте типы аргументов и позиции с привязываемой функцией.

Другие вопросы по тегам