Reputation: 16746
I'm using boost::bind to pass a handler function to boost::asio::async_write. When I use free functions, it works fine, but when I try to move the functions inside a class, bind produces errors that I am unable to decipher.
I write some data with:
boost::asio::async_write(*socket,
boost::asio::buffer(data(),
length()),
boost::bind(handlermessagewrite,
boost::asio::placeholders::error,
this,
boost::asio::placeholders::bytes_transferred));
Then I handle the write with a free function whose signature is:
void handlermessagewrite(const boost::system::error_code& errorcode,
iodata *msg,
size_t bytes_transferred);
This all works as expected.
I'm moving the handler inside a class 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);
and adapting the boost::bind code accordingly, as seen in the official asio tutorials:
- boost::bind(handlermessagewrite,
+ boost::bind(&ioclient::handlermessagewrite,
However, this produces some extremely opaque compile errors, not helped by the fact that one of the lines seems to end up truncated in my IDE (code::blocks):
\boost\bind\bind_template.hpp|102| required from 'boost::_bi::bind_t::result_type boost::_bi::bind_t::operator()(const A1&, const A2&) [with A1 = boost::system::error_code; A2 = unsigned int; R = void; 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| required from '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| \boost\asio\impl\write.hpp|585| required from '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| required from here| \boost\bind\bind.hpp|392|error: no match for call to '(boost::_mfi::mf2) (const boost::system::error_code&, iodata*&, const unsigned int&)'| \boost\bind\mem_fn_template.hpp|253|note: candidates are:| \boost\bind\mem_fn_template.hpp|278|note: R boost::_mfi::mf2::operator()(T*, A1, A2) const [with R = void; T = ioclient; A1 = const boost::system::error_code&; A2 = iodata*]| \boost\bind\mem_fn_template.hpp|278|note: no known conversion for argument 1 from 'const boost::system::error_code' to 'ioclient*'| \boost\bind\mem_fn_template.hpp|283|note: template R boost::_mfi::mf2::operator()(U&, A1, A2) const [with U = U; R = void; T = ioclient; A1 = const boost::system::error_code&; A2 = iodata*]| \boost\bind\mem_fn_template.hpp|283|note: template argument deduction/substitution failed:| \boost\bind\bind.hpp|392|note: cannot convert '(& 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))' (type 'iodata*') to type 'const boost::system::| \boost\bind\mem_fn_template.hpp|291|note: template R boost::_mfi::mf2::operator()(const U&, A1, A2) const [with U = U; R = void; T = ioclient; A1 = const boost::system::error_code&; A2 = iodata*]| \boost\bind\mem_fn_template.hpp|291|note: template argument deduction/substitution failed:| \boost\bind\bind.hpp|392|note: cannot convert '(& 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))' (type 'iodata*') to type 'const boost::system::| \boost\bind\mem_fn_template.hpp|299|note: R boost::_mfi::mf2::operator()(T&, A1, A2) const [with R = void; T = ioclient; A1 = const boost::system::error_code&; A2 = iodata*]| \boost\bind\mem_fn_template.hpp|299|note: no known conversion for argument 1 from 'const boost::system::error_code' to 'ioclient&'|
I'm convinced I'm doing something wrong with bind, but I'm at a loss as to what that may be. Any ideas?
Upvotes: 2
Views: 5893
Reputation: 51961
The initial example is failing because the object instance on which the member-function will be invoked is not passed to bind
. This is indicated in the compiler error where it states that there is no known conversion from const boost::system::error_code
to ioclient*
.
When using Boost.Bind with member pointers, it is documented that
boost::bind(&X::f, args)
is equivalent toboost::bind<R>(boost::mem_fn(&X::f), args)
.
Furthermore, Boost.mem_fn's documentation states:
It [
boost::mem_fn
] supports member function pointers with more than one argument, and the returned function object can take a pointer, a reference, or a smart pointer to an object instance as its first argument.
Thus, if this first argument of boost::bind
is a member-pointer, then either:
boost::mem_fn
.boost::bind
must be passed the object instance handle in the argument position matching the _1
placeholder.For example, given
struct Foo
{
void do_something(int x) {}
};
The do_something
member function could be bound and invoked with any of the following:
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.
With Boost.Asio, boost::asio::placeholders::error
is implemented as placeholder _1
. For this reason, the object instance must be passed to the bind
call as the second argument, or the object instance must be passed as an argument to either a free function or static member function, that will then invoke the member function on the object instance. Here is an example of solution that compiles with a non-static member function, but the snippet of interests is:
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));
The compiler error posted in the response to Lou indicates that an ioclient
member function is attempting to be invoked with an instance handle of iodata
, an incompatible type. For iodata
to be a compatible type, it must be inherited from ioclient
. If this is the intended type hierarchy, then verify that inheritance is correct. Otherwise, carefully match the argument types and positions with the function being bound.
Upvotes: 3
Reputation: 1955
When using a instance method, you must pass the this
pointer as the second argument to bind().
EDIT:
I've done what you are trying to do with boost::asio. Here is a snippet from my implementation:
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));
where HandleAsioWrite is method declared as follows:
void HandleAsioWrite(const boost::system::error_code& rErrorCode,
std::size_t nBytesTransferred);
Upvotes: 4