On Boost 1.66, Asio have deprecated the asio_handler_is_continuation
hook function, promoting usage of defer
function. It seems that defer
function is behaving exactly the same as post
when asio_handler_is_continuation==true. However, the way of using defer
is different from the way of using asio_handler_is_continuation
, and I am not sure how to properly use defer
.
EDIT: I think the sample below is too verbose to clearly express what i mean. Here's shorter example:
async_read_until(stream, read_buffer, "\r\n",
[](boost::system::error_code ec, std::size_t bytes_transferred)
{
if(!ec)
async_write(stream, write_buffer, some_handler);
})
Now when async_read_until
is completed, the lambda handler passed will be invoked using some means equivalent to boost::asio::post
. But async_write
inside the lambda handler is continuation from last async task, So I want to invoke the lambda handler using defer
to take adventage of optimization.
Is there any way to use defer
(instead of post
) to invoke the lambda handler in above example?
ORIGINAL POST: I am trying to write a simple initating function async_echo
similar to the one in beast document, except that the part that calls boost::asio::async_write
will be called as a continuation. To achieve this, prior intermediate operation boost::asio::async_read_until
must call the handler *this
as a continuation.
This is the part that I am referring in the async_echo example of the beast document:
template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
{
// Store a reference to our state. The address of the state won't
// change, and this solves the problem where dereferencing the
// data member is undefined after a move.
auto& p = *p_;
// Now perform the next step in the state machine
switch(ec ? 2 : p.step)
{
// initial entry
case 0:
// read up to the first newline
p.step = 1;
return boost::asio::async_read_until(p.stream, p.buffer, "\r", std::move(*this));
case 1:
// write everything back
p.step = 2;
// async_read_until could have read past the newline,
// use buffers_prefix to make sure we only send one line
return boost::asio::async_write(p.stream,
boost::beast::buffers_prefix(bytes_transferred, p.buffer.data()), std::move(*this));
case 2:
p.buffer.consume(bytes_transferred);
break;
}
// Invoke the final handler. The implementation of `handler_ptr`
// will deallocate the storage for the state before the handler
// is invoked. This is necessary to provide the
// destroy-before-invocation guarantee on handler memory
// customizations.
//
// If we wanted to pass any arguments to the handler which come
// from the `state`, they would have to be moved to the stack
// first or else undefined behavior results.
//
p_.invoke(ec);
return;
}
On pre-1.66 days, I could simply hook the function as follows:
template <Function, Handler>
friend bool asio_handler_is_continuation(echo_op<Function, Handler>* handler)
{
using boost::asio::asio_handler_is_continuation;
return handler.p_->step == 1 ||
asio_handler_is_continuation(std::addressof(handler.p_->handler()));
}
inside the declaration of echo_op
.
Starting from Boost 1.66, the code above is not likely to have any effect (without BOOST_ASIO_NO_DEPRECATION
macro). So I should be using defer
.
But since boost::asio::async_read_until
has a guarantee that "Invocation of the handler will be performed in a manner equivalent to using boost::asio::io_context::post().", *this
will not be invoked using defer
, that is, as a continuation.
Is there any workaround that makes boost::asio::async_read_until
invoke the handler using defer
? And is there any good examples that utilize defer
function?