Создайте PyObject * с указателем на объект C - PullRequest
0 голосов
/ 02 ноября 2019

Скажем, у меня есть такая структура:

typedef struct
{
  PyObject_HEAD
  Foo* myFoo;
} PyFoo;

Скажем так: Foo - это:

class Foo
{
public:
  hello()
  {
    std::cout << "Hello\n";
  }
};

Я не хочу переделывать класс Foo как модуль python, потому чтоон представляет класс из библиотеки с гораздо большим количеством функций и переменных (но это не имеет отношения к этому вопросу). Из документов я не совсем понял, как создать PyObject * в C / C ++ с аргументами, а тем более, как это сделать с указателями C / C ++ в качестве аргументов.

Я ухожу из этого руководства: https://docs.python.org/3/extending/newtypes_tutorial.html

У меня есть методы dealloc, new и init из руководства, но я не пытался инициализировать и освободить какие-либо значения, кроме экземпляра самого объекта.

Этот вопрос похож на Создать PyObject * из функции C? , но я хочу передать указатель объекта вместо функции. Я использую тот же метод, что и Создание объекта с использованием Python C API для создания объекта, но я не знаю, как я могу передать экземпляр foo объекту PyObject.

Ответы [ 2 ]

2 голосов
/ 03 ноября 2019

Я думаю, вы делаете вещи более сложными, чем нужно, пытаясь вызвать конструктор с указателем. Ваши методы tp_new и tp_init предназначены для предоставления интерфейса Python для создания экземпляра объекта. Если не имеет смысла предоставлять интерфейс Python (например, если ваш объект всегда должен быть создан с указателем C ++), просто не предоставляйте их - установите их на NULL, и ваш объект не будет создан изPython.

В C ++ вы не ограничены этим интерфейсом. Вы можете определить свою собственную "фабричную функцию C ++", используя любые аргументы, которые вам нравятся:

PyFoo* make_PyFoo(Foo* myfoo) {

Сначала выделите ваш объект:

PyFoo *obj = (PyFoo*)(type->tp_alloc(type, 0));
# or
PyFoo *obj = PyObject_New(PyFoo, type); # use PyObject_GC_new if it has cyclic references

Два подхода в значительной степени эквивалентны, если у вас нет 't определил пользовательский распределитель. Некоторая проверка ошибок здесь опущена ....

Далее вы можете просто использовать существующий указатель Foo* для инициализации соответствующего поля:

obj->myfoo = myfoo;

Затем просто return obj (и закройте скобку).


Этот ответ был во многом вдохновлен моей давней неприязнью к капсулам Python. Очень редко можно увидеть разумный вариант их использования, но людям все равно нравится их использовать.

0 голосов
/ 03 ноября 2019

Таким образом, вы можете сделать это с помощью капсул.

Когда вы создаете объект:

Foo* myFoo = new Foo();
PyObject* capsule = PyCapsule_New((void*) myFoo, "Foo", NULL);
PyObject* argList = Py_BuildValue("(O)", capsule);
PyObject *obj = PyObject_CallObject((PyObject*) &Foo_PyTypeObject, argList);
Py_DECREF(arglist);
Py_DECREF(capsule); // I don't need python to keep the reference to myFoo

Foo_PyTypeObject должен быть PyTypeObject, который вы создаете для своего объекта расширения.

Затем я использовал капсулу в своей «новой» функции.

Foo_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
{
  Foo* self;
  self = (Foo*) type->tp_alloc(type, 0);
  if (self != NULL)
  {
    static char *kwlist[] = {const_cast<char*>("Foo"), NULL};

    PyObject* capsule = NULL;
    PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &capsule);
    self->myFoo = (Foo*) PyCapsule_GetPointer(capsule, "Foo"); // this is the myFoo in the PyFoo struct
    Py_DECREF(capsule);
  }
  return (PyObject*) self;
}
...