I am specifically referring to the from_promise method.
https://en.cppreference.com/w/cpp/coroutine/coroutine_handle/from_promise
Would this function behave correctly (and in a portable way) if I tried to get a coroutine_handle from a reference to a SpecialisedPromise? Similar question with from_address
Code example:
struct Foo
{
/* ... */
};
struct GenericPromise
{
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
virtual ~GenericPromise() = default;
};
struct Coroutine
{
std::coroutine_handle<GenericPromise> handle;
struct promise_type : Foo, GenericPromise
{
Coroutine get_return_object()
{
return { std::coroutine_handle<GenericPromise>::from_promise(*this) };
}
int AdditionalData = 42;
};
};
Coroutine Coro()
{
co_return;
}
int main(int argc, char* argv[])
{
Coroutine coro = Coro();
coro.handle.resume();
assert(coro.handle.done());
coro.handle.destroy();
}
Is this program guaranteed to work as expected, or is the access through a different-typed coroutine_handle a potential bug/UB?
You can keep handle to the given coroutine - either with std::coroutine_handle<>
(which is std::coroutine_handle<void>
) or via std:::coroutine_handle<PromiseType>
where PromiseType
is exact type of your coroutines.
See what coroutine_handle::from_address cppreference says:
The behavior is undefined if addr is neither a null pointer value nor an underlying address of a coroutine_handle. The behavior is also undefined if the addr is an underlying address of a std::coroutine_handle, where both Promise and P1 are not void, and P1 is different from Promise.
Imagine that coroutine_handle is just some wrapper on void*
to promise object. In no coroutine world it is also UB when casting from P*
to void*
, then from void*
to P1*
if !std::is_same_v<P, P1>
And similarly in coroutine_handle::from_promise
The behavior is undefined if p is not a reference to a promise object.
So - it can't be reference to base subobject of coroutine - it has to be reference to the coroutine object - otherwise it is UB to use from_promise.
Anyway - in your case - I would consider to store std::coroutine_handle<>
and pointer to your "generic" base class:
struct Coroutine
{
std::coroutine_handle<> handle;
GenericPromise* genericPromise;
struct promise_type : Foo, GenericPromise
{
Coroutine get_return_object()
{
return { .handle = std::coroutine_handle<promise_type>::from_promise(*this),
.genericPromise = this };
}
int AdditionalData = 42;
};
};