Boost logo

Boost Users :

From: Cristian Morales Vega (cristian_at_[hidden])
Date: 2019-09-02 12:56:54


On Mon, 2 Sep 2019 at 10:30, Stephan Menzel via Boost-users
<boost-users_at_[hidden]> wrote:
> I'm puzzled about the double std::move() of self. In the comments above it says that self is a reference to an intermediate completion handler. How can this be moved twice?
> Can anyone give me hint about what this means?

There is anything in particular from composed_8.cpp that you find
confusing? The multiple moving thing is no different than in
composed_6.cpp or composed_7.cpp.
For the explanation I will mention line numbers, it may be easier to
look at https://github.com/boostorg/asio/blob/boost-1.71.0/example/cpp11/operations/composed_8.cpp

* In line 150 an async_write_messages_implementation object is created: objectA
* In line 89 objectA is moved into objectB.
  async_wait stores objectB. This should not surprise you, even if
boost::asio::async_compose hides the fact a bit, async_write_messages
is doing the same. Where is the lambda created in line 167 being
stored? It's being stored by async_write_messages. async_compose is
creating the class able to store such a lambda, and you are passing
the lambda to it in line 153 (as "token").
* After line 89 line 90 is NOT executed, the "yield" makes it leave
the "reenter" body. And since there is nothing else after that
operator() returns. objectA gets out of scope and destroyed here.
* After one second the wait finishes. And it executes
objectB.operator(). Once it reaches the "reenter" the magic of
boost::asio::coroutine (it's just a switch) makes it skip to line 90.
* In line 94 objectB is moved into objectC and by the same logic than
before objectB gets destroyed.
* Once async_write does its job objectC.operator() will be called
again, the reenter will continue from line 95 and finish the job.

So no object is moved multiple times, it's a different object each time.
Notice that's why in line 54 and 60 those objects are being created
with unique_ptr. If encoded_message_ were part of
async_write_messages_implementation it would be moved away, and the
async_write from line 93 could be writing the contents of a moved-from
std::string (it's undefined behaviour what exactly it would do).

> I would also appreciate any hints in general about own composed operations. Perhaps there are other approaches that are a bit less intimidating?

Once you get used to it it's not really that intimidating. All the
composed operations follow the same pattern and other than the
"operator()" it's a lot of repetitive boilerplate, quite boring
actually.

I know Boost.Beast is described as a "Portable HTTP, WebSocket, and
network operations using only C++11 and Boost.Asio" library and you
have no reason to look into it unless you need to HTTP/WebSocket. But
if you do you will find nowadays it's much more than that, it's also a
generic library on top of ASIO, slightly higher lever with very useful
helpers... and more user-friendly documentation.

If you look at https://www.boost.org/doc/libs/1_71_0/libs/beast/doc/html/index.html
you will find a whole "Writing Composed Operations" section in the
documentation.

And look at https://www.boost.org/doc/libs/1_71_0/libs/beast/doc/html/beast/ref/boost__beast__stable_async_base.html
What I said about "encoded_message_" having to be a pointer. The
example from the ASIO docs is actually slightly wrong (i.e.
simplified), that std::string should ideally be allocated with the
handler-associated allocator. "Allocators? I did once hear somebody
say the word, I don't know anything else about them" are you saying?
Don't worry, you just need to use Beast allocate_stable() and the
correct allocator will be used.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net