On Mon, 2 Sep 2019 at 10:30, Stephan Menzel via Boost-users
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/... * 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__b... 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.