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

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

При использовании метода экземпляра вы должны передать указатель
boost::asio::async_write(*socket,
                         boost::asio::buffer(data(),
                                             length()),
                         boost::bind(handlermessagewrite,
                                     boost::asio::placeholders::error,
                                     this,
                                     boost::asio::placeholders::bytes_transferred));
в качестве второго аргумента функции bind ().

Я перемещаю обработчик внутри класса 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):

Я убежден, что делаю что-то не так с привязкой, но не понимаю, что это может быть. Любые идеи?

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


person Riot    schedule 08.12.2012    source источник
comment
Конечно, я просмотрел документацию по привязке, а также другие источники и все соответствующие темы. Я нашел blog.think-async.com/2010/04/bind- illustrator.html очень полезный источник информации для связывания в целом, и, надеюсь, он может помочь и другим, однако это не решает мою проблему.   -  person Igor R.    schedule 08.12.2012
comment
Вы не можете так обмениваться аргументами. Первым аргументом результирующего функтора должен быть _1_, 2nd size_t - это 2 параметра, которые передает вызывающий объект (asio). Если ваша функция принимает больше параметров, они должны быть связаны в _2_ выражении.   -  person Riot    schedule 08.12.2012
comment
т.е. ваша функция должна выглядеть так: void error_code И привязка должна быть: bind   -  person Igor R.    schedule 08.12.2012
comment
Игорь: спасибо, но боюсь, что вы ошибаетесь. Можно организовать аргументы так, как я их имею, и, как я уже сказал в вопросе, первая сформулированная мною формулировка работает отлично. Вот диаграмма, демонстрирующая, как: think-async.com/blog/bind-illustrated/ У меня возникает проблема, когда я пытаюсь изменить бесплатную функцию на принадлежащую классу. Если бы порядок аргументов был проблемой, это не сработало бы и со свободной функцией, но это работает.   -  person Igor R.    schedule 08.12.2012
comment
ну, хотя этот продвинутый способ привязки определенно существует, я боюсь, что он довольно подвержен ошибкам, поэтому я бы рекомендовал написать его простым и понятным способом, чтобы вы были уверены, что порядок аргументов и типы совпадают. В частности, вы передаете _1_ вместо _2_, но OTOH передаете _3_ вместо _4 _...   -  person Riot    schedule 08.12.2012
comment
Хотя в сообщении об ошибке много шума, важной строкой является неизвестное преобразование для аргумента 1 из 'const boost :: system :: error_code' в 'ioclient * Как рекомендовал Игорь, я бы переписал его прямо способ и сначала скомпилируйте его, прежде чем пытаться изменить порядок аргументов и т. д. Кроме того, как сказал Лу, когда вы привязываетесь к функции-члену, указатель this должен быть аргументом. Кроме того, если это приводит к другому сообщению об ошибке компилятора, вам необходимо опубликовать это сообщение здесь, чтобы другие могли вам помочь.   -  person Igor R.    schedule 08.12.2012
comment
Так получилось, что я передаю указатель _1_ отдельному аргументу моего обработчика, а не как сеанс / контекст для связанной функции - поэтому он там не второй аргумент. Если я введу дополнительный указатель _2_ во второй позиции и сохраню или удалю свой аргумент _3_, компиляция все равно завершится ошибкой со следующими ошибками: pastebin.com/tFKUncm2   -  person Ralf    schedule 08.12.2012


Ответы (2)


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

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

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

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

Первоначальный пример терпит неудачу, потому что экземпляр объекта, для которого будет вызвана функция-член, не передается в this. Это указано в ошибке компилятора, где указано, что нет известного преобразования из

        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));
в
void HandleAsioWrite(const boost::system::error_code& rErrorCode,
                     std::size_t nBytesTransferred);
.

void HandleAsioWrite(const boost::system::error_code& rErrorCode,
                     std::size_t nBytesTransferred);
person Lou    schedule 08.12.2012
comment
@Riot У меня также возникают проблемы с интерпретацией ошибок вызова шаблона. Если вы передаете этот ptr как отдельный аргумент, сделайте метод статическим, и он также должен работать. Можете ли вы обновить вопрос с помощью полного вызова привязки, который вы пытаетесь заставить работать. Если вы пробовали два разных способа, покажите, пожалуйста. - person Riot; 08.12.2012
comment
\ boost \ bind \ 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 = беззнаковое целое; R = пусто; F = boost :: _ mfi :: mf2; L = boost :: _ bi :: list3 (*) (), boost :: _ bi :: value, boost :: arg ‹2> (*) ()>; boost :: _ bi :: bind_t :: result_type = void] '| \ boost \ asio \ impl \ write.hpp | 261 | требуется от 'void boost :: asio :: detail :: write_op :: operator () (const boost :: system :: error_code &, std :: size_t, int) [с AsyncWriteStream = boost :: asio :: basic_stream_socket; CompletionCondition = boost :: asio :: detail :: transfer_all_t; WriteHandler = boost :: _ bi :: bind_t, boost :: _ bi :: list3 (*) (), boost :: _ bi :: va | \ boost \ asio \ impl \ write.hpp | 585 | требуется от 'void boost :: asio :: async_write (AsyncWriteStream &, const ConstBufferSequence &, WriteHandler &&) [с 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; Т = ioclient; 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 = пусто; Т = ioclient; 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 | примечание: шаблон R boost :: _ mfi :: mf2 :: operator () (const U &, A1, A2) const [с U = U; R = пусто; Т = ioclient; 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; Т = ioclient; A1 = const boost :: system :: error_code &; A2 = йоданные *] | \ boost \ bind \ mem_fn_template.hpp | 299 | примечание: неизвестное преобразование для аргумента 1 из 'const boost :: system :: error_code' в 'ioclient &' | - person Lou; 08.12.2012

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

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

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

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

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

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

  • Объекту функции, возвращаемому из boost::bind, должен быть передан дескриптор экземпляра объекта в позиции аргумента, соответствующей заполнителю _1.
  • Почему бы вам не взглянуть на bind документацию? boost.org/doc/libs/1_52_0/libs/ привязать /

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

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

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

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.

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

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

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

person Tanner Sansbury    schedule 10.12.2012