Boost logo

Boost :

From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2023-11-09 15:01:38


czw., 9 lis 2023 o 15:09 Klemens Morgenstern <
klemensdavidmorgenstern_at_[hidden]> napisał(a):

> On Thu, Nov 9, 2023 at 9:41 PM Andrzej Krzemienski via Boost
> <boost_at_[hidden]> wrote:
>
>
> > In Boost.Cobalt (formerly Boost.Async) we have function cobalt::race
> > (formerly select()) that takes a range of awaitables `p` and returns an
> > awaitable capable on awaiting on one of the elements of `p`:
> >
> >
> https://www.boost.org/doc/libs/develop/libs/cobalt/doc/html/index.html#race-outline
> >
> > Example:
> >
> > ```
> > promise<string> talk_to_server_1();
> > promise<string> talk_to_server_2();
> > promise<string> timeout_with_default();
> >
> > vector<promise<int>> servers = { talk_to_server_1(), talk_to_server_2(),
> > timeout_with_default() };
> > pair<size_t, string> ans = co_await race(servers);
> > ```
> >
> > This obviously cannot work when the input range passed to `race` is
> empty.
>
> This example doesn't help, because this should obviously use the
> variadic version.
>
> The reason to use the ranged one is for when you have another
> component dictating what elements to listen to.
>
> try
> {
> vector<promise<int>> servers;
> for (const auto & server : config.get_servers())
> servers.push_back(talk_to_server(server));
>
> co_await race(servers);
> }
> catch(std::exception &)
> {
> // handle errors from running any server
> }
>
> That means the code using the ranged version is likely unaware of the
> actual value, because there's a very high likelihood it's not known at
> compile time.
>

Thanks Klemens. This is very useful information (when to use which
overload), and I recommend that it should be put in the documentation.

Stil, I do not feel convinced. Your example illustrates, I think, the
situation where an untrusted external input is passed unvalidated to the
guts of the program. I would never like to encourage or support this way of
writing servers. Of course, people will still do it, and it is prudent of
you to be prepared for it, but I would still strongly insist that your
library calls this usage a bug: that is something that the programmer
should fix as soon as they can. It doesn't prevent you from throwing
exceptions, but you have an opportunity to additionally discourage the use
like this (via warnings, crashes in debug mode, additional control flows in
debugger).

>
> > Hi Everyone,
> > I would like to discuss one correctness aspect of Boost.COBALT. We have
> > this list and the Slack channel, so I am not sure which place is better.
> > Let me do it here.
> >
>
> I don't see why this is a correctness issue and not a design choice.
> When is it correct to use an exception?
>

To some extent, this is your choice as a library author. But I am trying to
set up a guideline or a policy applicable to a range of libraries.
The uncontroversial circumstance that warrants a throw, is when a function
cannot deliver its promise, but the caller couldn't have known about it
when calling a function:

```
File f ("C:/folder/filename.cfg");
```
Here I checked that the file existed one millisecond ago. I requested for
it to be open, but the OS determined that the file is no longer there.
There was nothing I can do to guarantee that the file would be openable.

The case with `race` is different. There is a very simple way of checking
the empty-range, and it never can produce a meaningful result.

Regards,
&rzej;


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk