Ссылка на выделенный объект C ++ в pybind11

Я пытаюсь создать связывание Python с pybind11, который ссылается на экземпляр 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();
}

Я застрял на том, как создать связь между питоном 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 возможен другой подход, использующий пользовательскую упаковку конструктора: метод init python больше не требует вызова конструктора c ++. Вы можете сделать так, чтобы он возвращал экземпляр синглтона c ++ напрямую.

Декларация в вашем случае может выглядеть так:

   // Declare the singleton methods
py::class_<Singleton>(m, "Singleton")

.def("init", [](){
return std::unique_ptr<Singleton, py::nodelete>(&Singleton::instance());
});

В питоне:

myInstance1 = Singleton()
myInstance2 = Singleton()

myInstance1 и myInstance2 указывают на один и тот же объект C ++.

Это в основном тот же ответ, что и на этот другой вопрос.

1

Да, я знаю, что этот ответ довольно поздно, но ранее предоставленные решения либо устарели, либо решают проблему неясным образом.

Самая большая проблема с другими ответами заключается в том, что они используют pybind и сырой интерфейс Python одновременно. С pybind вы можете использовать более простой и приятный интерфейс для Интерпретатора.

После реализации, которая должна решить вашу проблему.

Первое, что вы заметите, это то, что мы используем заголовочный файл «embed.h».
Это дает нам функции для создания встроенных модулей.

Далее мы используем PYBIND11_EMBEDDED_MODULE вместо обычного PYBIND11_MODULE или устаревший PYBIND11_PLUGIN, Это макрос специально для встраивания.

Следующая интересная часть — это типы, которые мы определяем для нашей структуры. Кроме DogТипа мы также используем shared_ptr<Dog>, Это важно для работы с примерами. Когда main модуль выходит из области видимости и начинает очистку, он должен знать, что класс / структура имеет тип 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;
}
0
По вопросам рекламы [email protected]