|
Boost : |
From: Marcelo Zimbres Silva (mzimbres_at_[hidden])
Date: 2022-04-13 10:45:08
On Wed, 13 Apr 2022 at 01:29, VinÃcius dos Santos Oliveira
<vini.ipsmaker_at_[hidden]> wrote:
>
> https://github.com/boostorg/asio/blob/a7db875e4e23d711194bcbcb88510ee298ea2931/example/cpp17/coroutines_ts/chat_server.cpp#L84
> The abstraction is built for the specific application
> only. It'll carry its policies builtin.
Unless you factor out those application specific *policies* to end-up
with a generic *session*, that is not only useful on the chat_server,
but everywhere. I am claiming you can do that without losing
generality and without being incompatible with the *Asio the way*.
I find myself however repeating myself often, so I think we should
agree on something first, before the discussion gets even more
unintelligible. So let me start again
My Redis client class, which I call high-level-API, works to a large
extent just like the chat_session I pointed you at
but adapted for Redis and not a chat_server. So if you have a problem
with that example, we have to clarify before proceeding.
In that example you see a
- reader: a loop on async_read
- writer: a loop on async_write.
I am also claiming there is no way to avoid these loops because
- you have to keep constantly reading from a tcp socket for as long
as it is open, for obvious reasons.
- you need to loop on async_write to process the message queue.
If I don't provide these loops in my library, users will have to write
their own, simply because that is how communication of the tcp layer
works.
Now as to why you can't avoid callbacks. The reader implementation looks like
awaitable<void> reader()
{
for (std::string read_msg;;) {
std::size_t n = co_await boost::asio::async_read_until(...,
use_awaitable);
room_.deliver(read_msg.substr(0, n));
read_msg.erase(0, n);
}
}
As you see, the room_.deliver call is application specific and only
useful for the chat server. To make it useful elsewhere, you have to
call a user provided callback, so it becomes generic
// generic version.
awaitable<void> reader(callback ck)
{
for (std::string read_msg;;) {
std::size_t n = co_await boost::asio::async_read_until(...,
use_awaitable);
ck(msg);
read_msg.erase(0, n);
}
}
The same thing applies to the writer. In other words, high-level
applications that provide a reader and writer like that, will
invariably have to use a callback.
The biggest concern I had during development was whether co_yield were
a better way to deliver the data, instead of callback. That however
requires coroutine support and C++20 from all users. It is also an
annoyance as users would still have to write the loops themselves
again, so it is not a better solution.
Marcelo
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk