C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Coroutines: Add explicit initialization by "co_init" operator

From: Stefan Sichler <stsichler_at_[hidden]>
Date: Thu, 10 Aug 2023 00:10:14 +0200
Am 09.08.23 um 17:28 schrieb Jason McKesson via Std-Proposals:
>> - There is currently no way to implement a well-defined initialization of the promise_type from within the Coroutine's implementation body (and pass some parameters to the promise_type constructor).
>
> You're not supposed to. The promise needs to exist *before* the
> coroutine body. That means that meaningful initialization needs to
> already have happened.
>

Hm, for me that feels like a really serious limitation that was one of
the biggest problems for me. See the looong text in the end of the mail.

(...)

> Furthermore, the whole point of the coroutine system as designed is to
> *not care* if a function is or is not a coroutine. All of that
> machinery should be hidden, and the function body should look like
> "normal code" as much as possible.

I think we have a partly identical opinion here, but there is a
difference: For me, the function *signature* (i.e. function declaration)
should completely hide whether some function is a coroutine or not,
allowing me to also implement interfaces that don't care about coroutine
functionality under the hood.
But I actually don't see why the function *body* (i.e. function
definition) should hide that a function is a coroutine. As already
pointed out, the compiler is anyway urged to inject code at the very
beginning of the coroutine body, so why not make that explicity visile
at that point, but instead have that secretiveness here?

(...)

> If you like, you can change how the coroutine's return type maps to
> the promise type; the behavior you're citing is an overridable
> default.

I know. Please see the comment in my other mail.

> However, I don't buy the notion that
> `return_type::promise_type` being an accessible member of the return
> type violates the notion of it being an implementation detail.

I don't see the point here. If promise_type is in the return type and
the return type is part of the coroutine signature, then it is clearly
part of the signature which is somehow the exact opposite of an
implementation detail.
For example, I can't implement another coroutine with the same signatue,
but another promise_type, which is a common problem when implementing
interfaces, see the example below.


>
>> - The promise_type cannot have a member field of type of the return type, because the promise_type needs to be declared inside the return type, so the type definition of the return type is incomplete at that point.
>
> Why would the promise type need to have such a thing? If the two need
> to intercommunicate, then they should do so via the return type having
> a pointer/reference to the promise or a `coroutine_handle` or
> something else. After all, the promise *creates* the return value
> object; the promise has every opportunity to give it whatever it
> needs.

No, it hasn't, because it is still not initialized by the coroutine body
at that point. So I possibly can't pass all information that I needed to
the return value.

>
>> - The return object is created by promise_type::get_return_object() *before* the Coroutine implementation body had any chance of initializing the promise object by any means. (this was a major problem in my case that a had to work around in very ugly ways!)
>
> Can you explain in detail why that was a problem? Try to post code
> where you were doing this and having a problem. I'm getting the
> impression that you were trying to implement coroutine machinery in a
> way that clashes with its intended design.

Yes, I'm sure that the initial design didn't have my use case in mind ;-)

Ok, it's an extensive framework I'm working at, but I try to describe
the essential parts in a simplified fashion (and please see also my
other mail):

Imagine an I/O framwork, implementing I/Os on many different devices,
requiring very different actions in the background, but implementing
unified interfaces where possible.
All the I/Os in the background are NOT implemented in separate threads,
but are passed down to the hardare, which creates some kind of unified
"IoHandle" structure (like the OVERLAPPED structure in Windows) and
later can be checked for completion. These IoHandles are returned from
the I/O methods.
Regular process() calls on the IoHandle are then required to supervise
the progress of the I/Os until completion.

In such a framework, all IoHandles are of course bound to specific
devices, so the IoHandle needs to also encapsulate a pointer to the
device class instance the I/O was created by. This is an important point!

Now, I was trying to add coroutine functionality here and there, because
for some I/Os, it is actually required to compose them from several I/Os
with small code snippets to be executed in between them.
Note that this is a pretty perfect use case for coroutines (and I'm
waiting for that functionality since many years...)!!!

So what I did is adding the ability to IoHandle to optionally
encapsulate a coroutine handle and I then implemented some I/O functions
as coroutines returning such a handle.
Well, that sounds pretty simple, BUT as I already pointed out, IoHandle
is of course urged to store some pointer to the device instance.
Even that is a problem with the current coroutine specification! Let's
see why:
The method signature of the I/O member method in the device class looks
like this:

    virtual IoHandle do_something(blablabla...) override;

When this method is implemented as a coroutine, then IoHandle is created
by get_return_object() from the promise *before* any code in
do_something() is executed.
So, how should I now pass on the pointer to the device instance into the
IoHandle???

If I had a mandatory co_init operator in the method right at the
beginning of the function body, I would be able to pass a "this" pointer
to the initialization of the promise type which could be passed on into
my return obejct.

Do you see that point?
I reality of course, the code is much more complex, but I tried to
extract the essential points.

... and I'm sorry for the elongated explanation....!

Received on 2023-08-09 22:10:17