pt., 22 wrz 2023 o 17:02 Klemens Morgenstern < klemensdavidmorgenstern@gmail.com> napisał(a):
On Fri, Sep 22, 2023 at 10:34 PM Andrzej Krzemienski via Boost
wrote: Hi Everyone, In the context of the Boost review of Boost.Async, I wanted to share a thought on documentation. I think the Reference section in the docs is insufficient. A lot of libraries in Boost have this approach that the reference section of the documentation is a specification exhaustive enough that it could be used
to
provide a number of competing implementations. Each function has a very detailed contract: what it returns and when, what are the preconditions, what exceptions are thrown upon failure, what are the postconditions. I am missing this from the reference of Boost.Async.
Post-conditions are not really a good fit for asynchronous code I fear, especially since a user can omit a co_await. You'd end up with a confusing mess IMO.
I agree with this statement. (BTW, we have an entire paper on this: wg21.link/P2957) Yet, I still maintain that this documentation is lacking a description of what it expects and what it guarantees, even if you cannot call it "a postcondition on the function". Consider the specs for `select` as an example: https://klemens.dev/async/reference.html#select After reading it I am not sure I know what select() does, especially in the corner cases. I suppose that if I were familiar with other async frameworks from node.js or python I would know the answer. I am sure you want to give me a *guarantee* of some sort; maybe not on the call to select alone, but on the expression `co_await select(args...)`. So, what is it? The effect would be as if I called co_await on one (but it is not specified which) of the awaitables from `args...`. Did I guess that right? What happens when I call `co_await select(args...)` and all the awaitables in `args...` have already been co_awaited on? What happens if I pass zero arguments to `co_await select(args...)`? What happens if I pass an empty vector to `co_await select(args...)`? How do the results change if I pass a random number generator? How does the state of non-selected awaitables change after the call to `co_await select(args...)`? If the answer to any of these questions is "you mustn't do that", this should be listed as a precondition. But I might add, that I don't
like to read this kind of documentation either.
Also, it may be one of the first libraries (that meet a certain bar of documentation) tied so much to coroutines, so I have no strict
requirements
on how a coroutine library is documented, but I think things like promise types should be explicitly referenced.
I did this by having base types for each promise which are indeed documented
What I am missing is a section in Design part of documentation that says that a number of features are implemented as base classes that promise-types are expected to derive from. For a feature like "cancellation" or "cancellation state" I would expect a synopsis of class `promise_throw_if_cancelled_base` along with its function `await_transform` and the description of what the function does.
Let's consider `async::generator`. It is not only class template `generator`, but also:
1. the specialization of type trait std::coroutine_traits
It doesn't have that specialization.
My bad. But it has a public alias promise_type which has an effect on what guarantees I get.
2. Class template `generator_promise`
3. generator_promise
Not directly documented, but it's properties are listed
See here https://klemens.dev/async/reference.html#generator-promise
Yes, but I feel the docs should say it in a bit more detail. Like that it is the function `await_transform` of a specific class that does this or that. So that I can predict what is going to happen in my program.
4. generator_receiver
Why would this need to be documented? That's clearly an implementation detail.
You may be right here. I am having difficult times figuring out what is and what is not an implementation detail in the case of coroutine-based libraries.
5. awaitable types
Documented as a concept here: https://klemens.dev/async/reference.html#awaitable
Even though some of them belong to namespace `detail`, they provide guarantees relevant for the users. I do not think `generator_promise`is
an
implementation detail. I think it needs to be documented. This seems especially important when I start adding my awaitables to the mix.
It shouldn't be. The recommended way is to check for associators through concepts, as describe here:
I do not know what to make of this. I am not well familiar with associators yet. The docs says "async uses the associator concept of asio, but simplifies it." I read it as saying that one cannot add one's own awaitables until one understands the associator concept of ASIO. Still I am convinced that as a user of this library I need to know what is going on in `await_transform` of different types, and when I am engaging them. I hope this makes sense. I have little experience with coroutines, so I do not know which of the choices applied in Boost.Async are a necessary part of every C++ coroutine library, and which are unique choices specific to this one. Regards, &rzej;
I wonder what others think about it?
Regards, &rzej;
_______________________________________________ Unsubscribe & other changes: