redboltz / async_mqtt

Asynchronous MQTT communication library based on Boost.Asio
Boost Software License 1.0
77 stars 10 forks source link

coroutine async_send error_code return variable #316

Closed jbbjarnason closed 1 week ago

jbbjarnason commented 1 week ago

previously we could async_send with an error_code return variable,

example:

diff --git a/example/ep_cpp20coro_mqtt_client.cpp b/example/ep_cpp20coro_mqtt_client.cpp
index 2d982697..e555517a 100644
--- a/example/ep_cpp20coro_mqtt_client.cpp
+++ b/example/ep_cpp20coro_mqtt_client.cpp
@@ -40,7 +40,7 @@ proc(
         };

         // Send MQTT CONNECT
-        co_await amep->async_send(
+        std::error_code foo = co_await amep->async_send(
             am::v3_1_1::connect_packet{
                 true,   // clean_session
                 0x1234, // keep_alive

now with the following compile error:

FAILED: example/CMakeFiles/ep_cpp20coro_mqtt_client.dir/ep_cpp20coro_mqtt_client.cpp.o 
/usr/bin/clang++ -DASYNC_MQTT_USE_LOG -DBOOST_ATOMIC_DYN_LINK -DBOOST_ATOMIC_NO_LIB -DBOOST_CHRONO_DYN_LINK -DBOOST_CHRONO_NO_LIB -DBOOST_FILESYSTEM_DYN_LINK -DBOOST_FILESYSTEM_NO_LIB -DBOOST_LOG_DYN_LINK -DBOOST_LOG_NO_LIB -DBOOST_PROGRAM_OPTIONS_DYN_LINK -DBOOST_REGEX_DYN_LINK -DBOOST_REGEX_NO_LIB -DBOOST_THREAD_DYN_LINK -DBOOST_THREAD_NO_LIB -I/home/jonb/Projects/async_mqtt/include -g -std=c++20 -fcolor-diagnostics -MD -MT example/CMakeFiles/ep_cpp20coro_mqtt_client.dir/ep_cpp20coro_mqtt_client.cpp.o -MF example/CMakeFiles/ep_cpp20coro_mqtt_client.dir/ep_cpp20coro_mqtt_client.cpp.o.d -o example/CMakeFiles/ep_cpp20coro_mqtt_client.dir/ep_cpp20coro_mqtt_client.cpp.o -c /home/jonb/Projects/async_mqtt/example/ep_cpp20coro_mqtt_client.cpp
/home/jonb/Projects/async_mqtt/example/ep_cpp20coro_mqtt_client.cpp:43:25: error: no viable conversion from 'void' to 'std::error_code'
   43 |         std::error_code foo = co_await amep->async_send(
      |                         ^     ~~~~~~~~~~~~~~~~~~~~~~~~~~
   44 |             am::v3_1_1::connect_packet{
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   45 |                 true,   // clean_session
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~
   46 |                 0x1234, // keep_alive
      |                 ~~~~~~~~~~~~~~~~~~~~~
   47 |                 "ClientIdentifier1",
      |                 ~~~~~~~~~~~~~~~~~~~~
   48 |                 will,   // you can pass std::nullopt if you don't want to set the will message
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   49 |                 "UserName1",
      |                 ~~~~~~~~~~~~
   50 |                 "Password1"
      |                 ~~~~~~~~~~~
   51 |             },
      |             ~~
   52 |             as::use_awaitable
      |             ~~~~~~~~~~~~~~~~~
   53 |         );
      |         ~

But this works with lambdas, I am not sure the callpath is different between those tokens.

Is this supposed to work or was this feature removed?

redboltz commented 1 week ago

See https://redboltz.github.io/async_mqtt/doc/latest/tutorial/client.html Send MQTT CONNECT packet and start receive loop

redboltz commented 1 week ago

See also https://www.boost.org/doc/libs/1_85_0/doc/html/boost_asio/overview/composition/token_adapters.html

jbbjarnason commented 1 week ago

See https://redboltz.github.io/async_mqtt/doc/latest/tutorial/client.html Send MQTT CONNECT packet and start receive loop

I am not sure I follow, previously async_send or send had completion token for void(std::error_code) but now it is void()

To be more specific, the return type of this call https://github.com/redboltz/async_mqtt/blob/main/example/ep_cpp20coro_mqtt_client.cpp#L43 previously was std::error_code but now is void.

However async_send for callable completion token, lambda, has std::error_code completion token, see here: https://github.com/redboltz/async_mqtt/blob/main/example/ep_cb_mqtt_client.cpp#L67

redboltz commented 1 week ago

The CompletionToken parameter for async_send() is boost::system::error_code, not void.

The first boost::system::error_code is treated specially. When you use CompletionToken such as use_awaitable, the first boost::system::error_code is converted to the exception internally and removed from the parameter. This is asio style. All other asio function do that.

redboltz commented 1 week ago

If you don't want to exception, you can fix your code like this:

auto [ec] = co_await ep->async_send(
   ...,
   as::as_tuple(as::use_awaitable)
);
jbbjarnason commented 1 week ago

Interesting, did not remember that with asio thanks!!

redboltz commented 1 week ago

@jbbjarnason

Yes, at first, I thought it was strange. If the completion token has the signature void(boost::system::error_code ec1, boost::system::error_code ec2), then only ec1 is treated specially.

However, after studying Boost.Asio in depth, I found that this convention is designed working well with many other Boost.Asio mechanisms.