The GCC C++ compiler (any many other C++ compilers as well) provide nonstandard extentions such as
alloca()
for stack based allocationCan these be used inside of C++20 coroutines from a fundamental point of view? Is it possible at all? And if yes how is this implemented?
As far as I understood is that the C++20 coroutines generally create the stack-frame for the coroutine on the first call (i.e. when the promise object is created) and hence need to know the size of the coroutines stack-frame.
However this does not play nicely with alloca or other run-time dynamic stack allocation.
So is it possible and, if yes, how it is implemented? Or what are the implications?
int size means that size is a variable and C does not allow variable size arrays.
A variable length array can be used in a typedef statement. The typedef name will have only block scope. The length of the array is fixed when the typedef name is defined, not each time it is used. A function parameter can be a variable length array.
Safe and useful variable length arraysIt's safe because there's no arbitrary stack allocation. Pointers to arrays are a rare sight in C code, whether variable length or not.
Variable-length arrays GCC and C99 allow an array's size to be determined at run time. This extension is not permitted in standard C++. However, Clang supports such variable length arrays for compatibility with GNU C and C99 programs.
Unfortunately, alloca
is not compatible with C++20 coroutines in GCC. And the worst thing is that the compiler does not warn about it.
This code example demonstrates the incompatibility:
#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>{};
//unsigned a[1]; auto & i = a[0]; //this version works fine
auto & i = *new (alloca(sizeof(unsigned))) unsigned(0); //and this not
for (;; ++i) {
pp->value_ = &i;
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 << *promise.value_ << std::endl;
h();
}
h.destroy();
}
https://gcc.godbolt.org/z/8zG446Esx
It should print 0 1 2 3 4
, but does not precisely due to the usage of alloca
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With