必须为 C++20 协程框架保留多少内存?

cs-*_*cs- 18 c++ c++20 c++-coroutine

首先,我想预测我的代码的内存使用情况,就像任何负责任的程序员应该做的那样。即使我没有像我一样决定使用placement new分配我的协程框架,这也适用(见下面的伪代码)。即使假设我改变了对所有协程进行布局更新的想法,因此我让编译器将我所有的协程分配在堆上,我仍然希望 C++ 语言告诉我我将吃掉多少堆那。

但是,IRL,我的目标是高可靠性和嵌入式环境。有可能甚至一个堆,所以...

struct coroutine_return_type
{
  struct promise_type
  {
    void *operator new(std::size_t sz, char *buf, std::size_t szbuf)
    {
      if (sz > szbuf)
        throw std::bad_alloc{};
      return buf;
    }
    void operator delete(void *)
    {
    }
    // ...
  };
  // ...
};

coroutine_return_type my_coroutine(char *, std::size_t)
{
  // The arguments, char * and std::size_t,
  // have been fowrarded to promise_type::operator new
  // but here in the coroutine body they aren't used again...
  for ( ; ; )
    co_yield /* something */;
}

struct coroutine_instance_type
{
  char my_coroutine_frame[ /* WHAT? */ ];
  coroutine_return_type my_coroutine_instance;
  coroutine_instance_type()
    : my_coroutine_instance{my_coroutine(my_coroutine_frame, sizeof(my_coroutine_frame))}
  {
    // ...
  }
  // ...
};
Run Code Online (Sandbox Code Playgroud)

我想要的是

我想要一个编译时表达式来返回我的协程大小的上限,以替换/* WHAT? */.

愚蠢的解决方案

有一种明显愚蠢的方式(不完全)做我想做的事:

  1. 子类 std::bad_alloc。然后throw std::bad_alloc{}在我的operator new变成throw std::my_bad_alloc{sz}. catch 块可以调用my_bad_alloc_instance.get_parameter()以了解sz里面的内容operator new

  2. 调用my_coroutine(nullptr, 0)并捕获异常。

这有什么愚蠢之处(非详尽清单):

它不是编译时表达式,因为它必须使用 a 来“返回”其值throwthrow并且永远不能在编译时表达式中使用。但是/* WHAT? */我的伪代码中的替换需要是一个编译时表达式。

这是一个样本,而不是上限。假设协程帧的实际分配大小取决于运行时的条件。(现在,我不认为针对不同运行时条件的不同协程大小会实际出现在我的 IRL 应用程序中,但根据 C++ 标准,这似乎是可能的。)在这种情况下,仅仅学习是不够的实际传递给什么大小operator new。相反,所需的表达式必须返回可以传递给 的内容的上限operator new

所以,总结一下:

问题摘要

C++语言提供了哪些工具来查询协程框架的大小?理想的工具应该是用于将非堆内存分配给协程的编译时表达式,或者,相同的工具也可以用于限制堆的数量。

T.C*_*.C. 15

在 C++20 协程的标准化过程中,对此进行了长时间的辩论。协程框架的布局和大小在优化器完成其工作之前无法确定,并且使该信息可用于前端需要对所有现有编译器进行根本性的重新架构。实施者报告说,甚至(有用的)上限都不可行。

有关在不允许动态内存分配的环境中使用协程的方法的讨论,请参见P1365R0 的第2 部分和第 4 部分。