I'm adding support for coroutines ts in an async socket class based on windows io completion ports . Without coroutines the io may be done like this :
sock.async_write(io::buffer(somebuff), [](auto&& ... args){ /* in handler */ });
or
sock.async_write(std::vector<io::const_buffer>{ ... }, [](auto&& ... args){ /* in handler */ })
where each one will returns void and will notify the result through the handler and doesn't need to cache the parameters because the operation will have been submitted upon returning from the function
But with coroutines the function will return an awaitable that upon awaiting it with operator co_await will submit the operation so I need to cache the parameters in the awaitable to avoid using destructed temporaries :
awaitable coro_write(const io::const_buffer& buff)
{
return awaitable{ *this, buff };
}
awaitable coro_write(const std::vector<io::const_buffer>& buffs)
{
return awaitable{ *this, buffs };
}
the copy in the first one doesn't harm but in the second it does , cause it will trigger a heap allocation and copy the vector contents.
So I was searching for a solution to this and while reading this page coroutines ts I came across this :
A typical generator's yield_value would store (copy/move or just store the address of, since the argument's lifetime crosses the suspension point inside the co_await) its argument into the generator object and return std::suspend_always, transferring control to the caller/resumer.
and from the same page it is stated that co_yield expression is equivalent to :
co_await promise.yield_value(expr)
which is also similar to :
co_await sock.coro_write(expr)
I opened the generator header shipped with visual studio 2019 and saw that it also stored the address of the parameter to yield_value and retrieved it later through generator::iterator::operator *() in the caller site after the coroutine suspension :
struct promise_type {
_Ty const* _CurrentValue;
auto yield_value(_Ty const& _Value) {
_CurrentValue = _STD addressof(_Value);
return suspend_always{};
}
}
struct iterator {
_NODISCARD reference operator*() const {
return *_Coro.promise()._CurrentValue;
}
_NODISCARD pointer operator->() const {
return _Coro.promise()._CurrentValue;
}
}
from this I concluded that the parameter passed to the function that returns an awaiter used with co_await will also remain valid until the coroutine is resumed or destoryed , is this right ? or this is special for yield_value in a promise type ?