PYBND11:将类所有权转移到C++上

2024-10-01 15:48:04 发布

您现在位置:Python中文网/ 问答频道 /正文

我遇到了一个问题,即pybind11从c++基类派生的python类被立即销毁(垃圾回收)。我想C++对动态分配的对象拥有所有权,但我似乎无法实现。我尝试了keep-alive,将shared-ptr<;>;作为py::class_Utemplate参数和py::return_value_策略传递。。。什么都没用。我怀疑这只是用户错误。在

这是一个实际问题的简化,我有一个更大的代码库,架构类似。改变体系结构不是一个选择,所以让这个例子起作用对我来说至关重要。在

我有两个c++类,我已经为使用pybind11创建了python接口。类A和B都有虚方法,所以它们有相应的蹦床类来支持继承。用户调用B::Run()函数,该函数将生成一个动态分配(通过新建)的对象。当我在python中创建这两个类的专门化时,如下所示。。。。分段错误,因为B::aBase在调用B::Run后立即被破坏。在

有什么办法解决这个问题吗?提前谢谢!在

class A
{
public:
    A(){};
    virtual ~A()
    {
        std::cout << "In A::~A()\n";
    };

    virtual char* SayHello()
    {
        char* x = "\n\nHello from Class A\n\n";
        return x;
    }
};

class ATramploline : public A
{
public:
    using A::A;
    char* SayHello() override
    {
        PYBIND11_OVERLOAD( char*,A,SayHello,);
    }
};


class B
{
public:
    B()
    {
        std::cout << "In Class B Constructor\n";
    }

    void Run()
    {
        aBase = AllocateAnAClass();
        std::cout << aBase->SayHello();
    }

    virtual ~B()
    {
        fprintf(stderr,"About to delete aBase");
        delete aBase;
    }

    A* aBase;

    virtual A* AllocateAnAClass()
    {
        return new A;
    }
};

class BTramploline : public B
{
public:
    using B::B;
    A* AllocateAnAClass() override
    {
        PYBIND11_OVERLOAD( A*,B,AllocateAnAClass,);
    }
};

PYBIND11_MODULE(TestModule,m)
{
    py::class_<A,ATramploline>(m,"A")
        .def(py::init<>(),py::return_value_policy::reference_internal)
        .def("SayHello",&A::SayHello);

    py::class_<B,BTramploline>(m,"B")
        .def(py::init<>())
        .def("Run",&B::Run)
        .def("AllocateAnAClass",&B::AllocateAnAClass,py::return_value_policy::reference_internal);
}



#!/usr/bin/python3

from TestModule import A,B
class MyA(A):
    def __init__(self):
        super().__init__()
        print("Done with MyA Constructor")

    def SayHello(self):
        return '\n\nHello from Class MyA\n\n'

class MyB(B):
    def __init__(self):
        super().__init__()
        print("Done With MyB Constructor")
    def AllocateAnAClass(self):
        print("In MyB::AllocateAnAClass!!!")
        return MyA()

#x = B()
#x.Run()

y = MyB()
y.Run()

print("done with test script\n")

Tags: runpyselfreturninitdefvirtualpublic
2条回答

使用std::shared_ptr<A>作为A持有者的正确方法是将其添加到class_<A...>参数中。在

您还需要将A*的每个实例替换为std::shared_ptr<A>,并将{}替换为std::make_shared。我认为在这种情况下不需要非违约退货政策,所以我删除了它们;YMMV。在

以下为工作模块(修正了一些小错误)。在

#include <pybind11/pybind11.h>
#include <memory>
#include <iostream>

namespace py = pybind11;

class A
{
public:
    A(){};
    A(const A&) { std::cout << "Copying A\n"; }
    virtual ~A()
    {
        std::cout << "In A::~A()\n";
    };

    virtual const char* SayHello()
    {
        const char* x = "\n\nHello from Class A\n\n";
        return x;
    }
};

class ATrampoline : public A
{
public:
    using A::A;
    const char* SayHello() override
    {
        PYBIND11_OVERLOAD( const char*,A,SayHello,);
    }
};


class B
{
public:
    B()
    {
        std::cout << "In Class B Constructor\n";
    }

    B(const B&) { std::cout << "Copying B\n"; }
    void Run()
    {
        aBase = AllocateAnAClass();
        std::cout << aBase->SayHello();
    }

    virtual ~B()
    {
    }

    std::shared_ptr<A> aBase;

    virtual std::shared_ptr<A> AllocateAnAClass()
    {
        return std::make_shared<A>();
    }
};

class BTrampoline : public B
{
public:
    using B::B;
    std::shared_ptr<A> AllocateAnAClass() override
    {
        PYBIND11_OVERLOAD(std::shared_ptr<A>,B,AllocateAnAClass,);
    }
};

PYBIND11_MODULE(TestModule,m)
{
    py::class_<A,std::shared_ptr<A>, ATrampoline>(m,"A")
        .def(py::init<>())
        .def("SayHello",&A::SayHello);

    py::class_<B, BTrampoline>(m,"B")
        .def(py::init<>())
        .def("Run",&B::Run)
        .def("AllocateAnAClass",&B::AllocateAnAClass);
}

nodelet是解决方案。虽然n.m的答案是可行的,但它需要返回并将现有库中的所有指针更改为智能指针,这对我来说不是一个可行的选择。使用py::nodelete可以让我在pybind11方面做任何事情。在

py::class_<A,ATramploline,std::unique_ptr<A,py::nodelete> >(m,"A")
        .def(py::init<>())
        .def("SayHello",&A::SayHello);

相关问题 更多 >

    热门问题