Pybind11:在 Construct 上将 Class 所有权转移给 C++
Pybind11: Transfer Class Ownership to C++ on Construct
我遇到一个问题,python class 使用 pybind11 从 c++ 基础 class 派生,立即被破坏(收集垃圾)。我希望 C++ 获得动态分配对象的所有权,但我似乎无法做到这一点。我试过 keep_alive,将 shared_ptr<> 作为 py::class_ 模板参数传递,并且 py::return_value_policy... 没有任何效果。我怀疑这只是用户错误。
这是对我遇到的实际问题的简化,它具有类似架构的更大的代码库。更改架构不是一种选择,因此让这个示例工作对我来说至关重要。
我创建了两个 c++ classes python 接口来使用 pybind11。 Class A 和 B 都有虚方法,所以它们有对应的 trampoline classes 来支持继承。用户调用 B::Run() 函数,这会导致动态分配(通过 new)创建一个对象。当我在 python 中创建这两个 class 的特化时,如下所示....分段错误,因为 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")
使用 std::shared_ptr<A>
作为 A
持有者的正确(我认为)方法是将其添加到 class_<A...>
参数中。
您还想将 A*
的每个实例替换为 std::shared_ptr<A>
,将 new
的每个实例替换为 std::make_shared
。我认为在这种情况下不需要 non-default return 政策,因此我已将其删除; 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);
}
py::nodelete 是解决方案。虽然 n.m 的答案确实有效,但它需要返回并将现有库中的所有指针更改为智能指针,这对我来说不是一个可行的选择。使用 py::nodelete 允许我在 pybind11 端做所有事情。
py::class_<A,ATramploline,std::unique_ptr<A,py::nodelete> >(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);
我遇到一个问题,python class 使用 pybind11 从 c++ 基础 class 派生,立即被破坏(收集垃圾)。我希望 C++ 获得动态分配对象的所有权,但我似乎无法做到这一点。我试过 keep_alive,将 shared_ptr<> 作为 py::class_ 模板参数传递,并且 py::return_value_policy... 没有任何效果。我怀疑这只是用户错误。
这是对我遇到的实际问题的简化,它具有类似架构的更大的代码库。更改架构不是一种选择,因此让这个示例工作对我来说至关重要。
我创建了两个 c++ classes python 接口来使用 pybind11。 Class A 和 B 都有虚方法,所以它们有对应的 trampoline classes 来支持继承。用户调用 B::Run() 函数,这会导致动态分配(通过 new)创建一个对象。当我在 python 中创建这两个 class 的特化时,如下所示....分段错误,因为 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")
使用 std::shared_ptr<A>
作为 A
持有者的正确(我认为)方法是将其添加到 class_<A...>
参数中。
您还想将 A*
的每个实例替换为 std::shared_ptr<A>
,将 new
的每个实例替换为 std::make_shared
。我认为在这种情况下不需要 non-default return 政策,因此我已将其删除; 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);
}
py::nodelete 是解决方案。虽然 n.m 的答案确实有效,但它需要返回并将现有库中的所有指针更改为智能指针,这对我来说不是一个可行的选择。使用 py::nodelete 允许我在 pybind11 端做所有事情。
py::class_<A,ATramploline,std::unique_ptr<A,py::nodelete> >(m,"A")
.def(py::init<>())
.def("SayHello",&A::SayHello);