首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ 20协同线与PyBind11

C++ 20协同线与PyBind11
EN

Stack Overflow用户
提问于 2022-03-10 11:29:42
回答 1查看 400关注 0票数 0

我试图让一个简单的基于C++ 20的生成器模式与PyBind11一起工作。这是代码:

代码语言:javascript
复制
#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还不支持的低层次的东西?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-22 00:33:44

尽管PyBind11不直接支持coroutines,但您的问题并不混合coroutine和pybind代码,因为您无论如何都在Gen后面隐藏coroutine。

问题是您的Generator2类型使用编译器提供的复制和移动构造函数。

这一行:

代码语言:javascript
复制
myCoroutineResult = myCoroutineFunction();

在调用myCoroutineFunction时创建一个协同处理句柄,并将其放在右手侧的临时Generator2中。然后,从右侧生成器初始化myCoroutineResult。一切都很好,但是临时的被摧毁了。析构函数检查句柄是否有效:

代码语言:javascript
复制
~Generator2() {
    if(coro)
        coro.destroy();
}

但是在您的实现中,成员生成器的coro成员在没有重置临时成员的情况下从临时成员中复制。因此,一旦初始化myCoroutineResult,协同线本身就会被破坏,并且您将持有一个悬空的协同线句柄。记住,std::coroutine_handle的行为就像一个原始指针。

本质上,您违反了5的规则。您有一个自定义析构函数,但没有复制/移动构造函数或赋值运算符。由于不能复制构造协同线,所以可以忽略复制构造函数,但需要提供移动构造函数/辅助运算符:

代码语言:javascript
复制
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;
}

此外,使用成员初始化列表初始化成员,而不是在构造函数主体中分配成员。所以,不是这样的:

代码语言:javascript
复制
Gen(){
    myCoroutineResult = myCoroutineFunction();
}

用这个:

代码语言:javascript
复制
Gen() : myCoroutineResult{myCoroutineFunction()} {}

即使在这个答案中也可以看出理由。第一个调用赋值操作符,它执行一系列额外的工作,而第二个调用移动构造函数,它是尽可能瘦的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71423398

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档