在 pybind11 中引用 C++ 分配的对象
Posted
技术标签:
【中文标题】在 pybind11 中引用 C++ 分配的对象【英文标题】:Referencing a C++ allocated object in pybind11 【发布时间】:2016-10-04 19:36:03 【问题描述】:我正在尝试使用 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 example\n"
"\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
的新实例,这不是我想要的。
【问题讨论】:
【参考方案1】:我找到了自己问题的答案。诀窍是结合了以下两个概念:
创建一个返回单例的独立函数。 创建与单例类的绑定而不绑定构造函数。以下示例说明了该技术:
#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 example\n"
"\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);
【讨论】:
【参考方案2】:从 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++ 对象。
基本上就是the same answer as that other question。
【讨论】:
【参考方案3】:是的,我知道这个答案已经很晚了,但是之前提供的解决方案要么已经过时,要么以一种晦涩难懂的方式解决了问题。
其他答案的最大问题是他们同时使用 pybind 和原始 Python 接口。使用 pybind,您可以使用更简单、更好的解释器界面。
遵循应该可以解决您的问题的实现。
您会注意到的第一件事是我们使用了“embed.h”头文件。 这为我们提供了创建嵌入式模块的函数。
再往下,我们使用PYBIND11_EMBEDDED_MODULE
而不是常规的PYBIND11_MODULE
或过时的PYBIND11_PLUGIN
。这是一个专门用于嵌入的宏。
下一个有趣的部分是我们为结构定义的类型。除了Dog
Type,我们还使用shared_ptr<Dog>
。这对于处理实例至关重要。当main
模块超出范围并开始清理时,它需要知道 Class/Struct 的类型为 shared_ptr 否则你会得到一个段错误(原始指针在这里不可用,我个人认为这是一件好事) .
最后要指出的是,我们实际上使用 pybind11::scoped_interpreter
类作为解释器,而不是使用 Raw Python 接口。
#include"pybind11\pybind11.h"
#include"pybind11\embed.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;
【讨论】:
以上是关于在 pybind11 中引用 C++ 分配的对象的主要内容,如果未能解决你的问题,请参考以下文章
Pybind11:从 C++ 端创建并返回 numpy 数组