Reputation: 21307
If I return address of a local coroutine variable, e.g. via the promise, is it guaranteed to work by C++ standard? Consider an example (based on https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html):
#include <coroutine>
#include <iostream>
struct ReturnObject {
struct promise_type {
unsigned * value_ = nullptr;
void return_void() {}
ReturnObject get_return_object() {
return {
.h_ = std::coroutine_handle<promise_type>::from_promise(*this)
};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() {}
};
std::coroutine_handle<promise_type> h_;
operator auto() const { return h_; }
};
template<typename PromiseType>
struct GetPromise {
PromiseType *p_;
bool await_ready() { return false; }
bool await_suspend(std::coroutine_handle<PromiseType> h) {
p_ = &h.promise();
return false;
}
PromiseType *await_resume() { return p_; }
};
ReturnObject counter()
{
auto pp = co_await GetPromise<ReturnObject::promise_type>{};
for (unsigned i = 0;; ++i) {
pp->value_ = &i; // is it legal?
co_await std::suspend_always{};
}
}
int main()
{
std::coroutine_handle<ReturnObject::promise_type> h = counter();
auto &promise = h.promise();
for (int i = 0; i < 5; ++i) {
std::cout << "counter: " << *promise.value_ << std::endl;
h();
}
h.destroy();
}
https://gcc.godbolt.org/z/P5PMc15qW
On practice I see that it does work, but is it really legal?
Upvotes: 4
Views: 305
Reputation: 63039
Yes, this is legal. The local variables of counter
are stored in a dynamically allocated object that h
owns.
The usual caveats of the possibility of use-after-free are there, i.e. promise
dangles after h.destroy()
.
Upvotes: 4
Reputation: 9
It can work in some cases but this is not as good way to write programs. Memory cell pointed to by the pointer can be (and it will probably be soon) overwritene by other variables. Try to write some code (declare some variables) before loop and check results again.
Upvotes: -3