我试图让一个简单的基于C++ 20的生成器模式与PyBind11一起工作。这是代码:
#include <pybind11/pybind11.h>
#include <coroutine>
#include <iostream>
struct Generator2 {
Generator2(){}
struct Promise;
using promise_type=Promise;
std::coroutine_handle<Promise> coro;
Generator2(std::coroutine_handle<Promise> h): coro(h) {}
~Generator2() {
if(coro)
coro.destroy();
}
int value() {
return coro.promise().val;
}
bool next() {
std::cout<<"calling coro.resume()";
coro.resume();
std::cout<<"coro.resume() called";
return !coro.done();
}
struct Promise {
void unhandled_exception() {std::rethrow_exception(std::move(std::current_exception()));}
int val;
Generator2 get_return_object() {
return Generator2{std::coroutine_handle<Promise>::from_promise(*this)};
}
std::suspend_always initial_suspend() {
return {};
}
std::suspend_always yield_value(int x) {
val=x;
return {};
}
std::suspend_never return_void() {
return {};
}
std::suspend_always final_suspend() noexcept {
return {};
}
};
};
Generator2 myCoroutineFunction() {
for(int i = 0; i < 100; ++i) {
co_yield i;
}
}
class Gen{
private:
Generator2 myCoroutineResult;
public:
Gen(){
myCoroutineResult = myCoroutineFunction();
}
int next(){
return (myCoroutineResult.next());
}
};
PYBIND11_MODULE(cmake_example, m) {
pybind11::class_<Gen>(m, "Gen")
.def(pybind11::init())
.def("next", &Gen::next);
}然而,我得到了一个错误:
处理完成了出口代码139 (被信号11: SIGSEGV中断)
c++协同器、coroutine_handles、co_yield等是否是PyBind11还不支持的低层次的东西?
发布于 2022-03-22 00:33:44
尽管PyBind11不直接支持coroutines,但您的问题并不混合coroutine和pybind代码,因为您无论如何都在Gen后面隐藏coroutine。
问题是您的Generator2类型使用编译器提供的复制和移动构造函数。
这一行:
myCoroutineResult = myCoroutineFunction();在调用myCoroutineFunction时创建一个协同处理句柄,并将其放在右手侧的临时Generator2中。然后,从右侧生成器初始化myCoroutineResult。一切都很好,但是临时的被摧毁了。析构函数检查句柄是否有效:
~Generator2() {
if(coro)
coro.destroy();
}但是在您的实现中,成员生成器的coro成员在没有重置临时成员的情况下从临时成员中复制。因此,一旦初始化myCoroutineResult,协同线本身就会被破坏,并且您将持有一个悬空的协同线句柄。记住,std::coroutine_handle的行为就像一个原始指针。
本质上,您违反了5的规则。您有一个自定义析构函数,但没有复制/移动构造函数或赋值运算符。由于不能复制构造协同线,所以可以忽略复制构造函数,但需要提供移动构造函数/辅助运算符:
Generator2(Generator2&& rhs) : coro{std::exchange(rhs.coro, nullptr)} {
// rhs will not delete our coroutine,
// since we put nullptr to its coro member
}
Generator2& operator=(Generator2&& rhs) {
if (&rhs == this) {
return *this;
}
if (coro) {
coro.destroy();
}
coro = std::exchange(rhs.coro, nullptr);
return *this;
}此外,使用成员初始化列表初始化成员,而不是在构造函数主体中分配成员。所以,不是这样的:
Gen(){
myCoroutineResult = myCoroutineFunction();
}用这个:
Gen() : myCoroutineResult{myCoroutineFunction()} {}即使在这个答案中也可以看出理由。第一个调用赋值操作符,它执行一系列额外的工作,而第二个调用移动构造函数,它是尽可能瘦的。
https://stackoverflow.com/questions/71423398
复制相似问题