在pybind11中引用c++分配的对象
Referencing a C++ allocated object in pybind11
我试图用pybind11创建一个python绑定,该绑定引用了一个c++实例,其内存在c++端处理。下面是一些示例代码:
import <pybind11/pybind11>
struct Dog {
void bark() { printf("Bark!n"); }
};
int main()
{
auto dog = new Dog;
Py_Initialize();
initexample(); // Initialize the example python module for import
// TBD - Add binding between dog and example.dog .
PyRun_StringFlags("import examplen"
"n"
"example.dog.bark()n" // Access the C++ allocated object dog.
, Py_file_input, main_dict, main_dict, NULL);
Py_Finalize();
}
我被困在如何创建python example.dog
和c++ dog
变量之间的链接。
我不能使用py:class_<Dog>.def(py::init<>())
,因为这将分配一个新的Dog
实例,这不是我想要的。
我找到了自己问题的答案。诀窍是结合了以下两个概念:
- 创建一个返回单例的独立函数
- 创建到单例类的绑定,而不需要绑定构造函数。
下面的示例说明了该技术:
#include <Python.h>
#include <pybind11/pybind11.h>
namespace py = pybind11;
using namespace pybind11::literals;
// Singleton to wrap
struct Singleton
{
Singleton() : x(0) {}
int exchange(int n) // set x and return the old value
{
std::swap(n, x);
return n;
}
// Singleton reference
static Singleton& instance()
{
static Singleton just_one;
return just_one;
}
int x;
};
PYBIND11_PLUGIN(example) {
py::module m("example", "pybind11 example plugin");
// Use this function to get access to the singleton
m.def("get_instance",
&Singleton::instance,
py::return_value_policy::reference,
"Get reference to the singleton");
// Declare the singleton methods
py::class_<Singleton>(m, "Singleton")
// No init!
.def("exchange",
&Singleton::exchange,
"n"_a,
"Exchange and return the current value"
)
;
return m.ptr();
}
int main(int argc, char **argv)
{
Py_Initialize();
PyObject* main_module = PyImport_AddModule("__main__");
PyObject* main_dict = PyModule_GetDict(main_module);
initexample();
// Call singleton from c++
Singleton::instance().exchange(999);
// Populate the example class with two static pointers to our instance.
if (PyRun_StringFlags("import examplen"
"n"
"example.s1 = example.get_instance()n"
"example.s2 = example.get_instance()n",
Py_file_input, main_dict, main_dict, NULL) == nullptr)
PyErr_Print();
// Test referencing the singleton references
if (PyRun_StringFlags("from example import *n"
"n"
"for i in range(3):n"
" print s1.exchange(i*2+1)n"
" print s2.exchange(i*2+2)n"
"print dir(s1)n"
"print help(s1.exchange)n"
,
Py_file_input, main_dict, main_dict, NULL) == nullptr)
PyErr_Print();
Py_Finalize();
exit(0);
}
自Pybind11 v2.2.0以来,另一种方法是可能的,使用自定义构造函数包装:python init方法不再需要调用c++构造函数。你可以让它直接返回c++的单例实例。
声明在你的例子中可能看起来像:
// Declare the singleton methods
py::class_<Singleton>(m, "Singleton")
.def("init", [](){
return std::unique_ptr<Singleton, py::nodelete>(&Singleton::instance());
});
在python中:
myInstance1 = Singleton()
myInstance2 = Singleton()
myInstance1和myInstance2指向同一个c++对象。
是的,我知道这个答案很晚了,但是之前提供的解决方案要么过时了,要么用一种模糊的方式解决问题。
其他答案的最大问题是它们同时使用pybind和原始Python接口。使用pybind,你可以使用一个更简单、更好的接口来连接解释器。
遵循可以解决问题的实现
你会注意到的第一件事是我们使用了"embed.h"头文件。这为我们提供了创建嵌入式模块的函数。
再往下,我们使用PYBIND11_EMBEDDED_MODULE
代替常规的PYBIND11_MODULE
或过时的PYBIND11_PLUGIN
。这是一个专门用于嵌入的宏。
Dog
类型,我们还使用shared_ptr<Dog>
类型。这对于处理实例是至关重要的。当main
模块超出作用域并开始清理时,它需要知道类/结构是shared_ptr类型的,否则你会得到一个seg错误(原始指针在这里不可用,我个人认为这是一件好事)。
最后要指出的是,我们实际上使用pybind11::scoped_interpreter
类作为解释器,而不使用Raw Python接口。
#include"pybind11pybind11.h"
#include"pybind11embed.h"
#include<iostream>
namespace py = pybind11;
struct Dog {
void bark() { std::cout << "Bark!n"; }
};
PYBIND11_EMBEDDED_MODULE(DogModule, m) {
// look at the types! we have a shared_ptr<Dog>!
py::class_<Dog, std::shared_ptr<Dog>>(m, "DogModule")
.def("bark", &Dog::bark);
}
int main(int argc, char **argv)
{
// Create Python Interpreter
py::scoped_interpreter guard;
// Create Dog Instance
std::shared_ptr<Dog> ptr = std::make_shared<Dog>();
// Import the DogModule & Assign the instance to a name in python
py::module main = py::module::import("__main__");
main.import("DogModule");
main.attr("dogInstance") = ptr;
// Call the bark method from python
py::exec("dogInstance.bark()");
getchar();
return 0;
}
相关文章:
- 使用操作重载对象重新分配对象
- 静态分配对象的值初始化
- C++ 将抽象类型的动态分配对象传递给函数并存储在向量中
- 堆分配对象中的堆栈对象在 c++ 中在哪里分配?
- 在 c++ 的构造函数中分配对象向量时出错
- 动态分配对象中的字段-动态分配更好还是静态分配更好?C++
- 操作后通过运算符分配对象
- 如何捕获源自静态分配对象的构造函数的异常?
- 在C++中,当重新分配对象时,为什么构造函数在析构函数之前触发?
- 如何使用每个对象的单个构造函数参数动态分配C++对象数组?
- 将动态分配对象传递到 boost::any 构造函数中
- 混合指向已分配对象和作用域对象的指针
- C 删除指向动态分配对象的指针
- 有关动态分配对象的问题
- 类的堆分配对象是否在其作用域之后但在 C++ 中调用其析构函数之前处于活动状态
- C++ 中的黑白堆分配对象和堆栈分配对象的性能差异
- 是隐式创建的默认构造函数,负责分配对象内存
- 如何删除用于动态分配对象的智能指针
- 分配对象数组时如何初始化每个对象
- 用于堆栈分配对象的C++虚拟析构函数内联