是否可以将 C++ 指针分配给 Python 对象的属性,并在另一个 C++ 函数中将其检索为 C++ 指针?

Is it possible to assign an C++ pointer to an attribute of a Python object, and retrieve it as a C++ pointer in another C++ function?

我正在为 Python 做一个 C++ 扩展,我正在尝试做类似的事情:

// this function assigns a C++ pointer to as attribute of a python object
void function1(PyObject* p){
    // equivalent of p.attr = cpp_attr;
    MyClass* cpp_attr = new MyClass();
    PyObject* args = PyTuple_Pack(cpp_attr);
    PyObject_SetAttrString(p, (char*)"attr", args);
}

我想检索此指针并将其设置为另一个 C++ 对象的属性。我知道如何获得 PyObject* 但在那之后我不知道该怎么做

MySecondClass::MySecondClass(PyObject* p){
    // get the attribute from p; equivalent of cpp_attr = p.attr
    PyObject* cpp_attr = PyObject_getAttrString(p, (char*)"attr"));
    // somehow get back the pointer to MyClass object created in function1
    
}

我查看了文档,但找不到任何 returns 原始类型。有办法吗?

谢谢

假设您对 PyTuple_Pack 的调用是正确的,那么您已经创建了一个 PyTupleObject,它有一个 structure:

typedef struct {
  PyObject_VAR_HEAD
  PyObject *ob_item[1];
} PyTupleObject;

PyTupleObject 继承自泛型 PyObject struct,它具有以下成员:

struct _object *_ob_next;
struct _object *_ob_prev;
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;

您可以使用宏访问后两者Py_REFCNT and Py_TYPE

ob_item[1] 成员应该是指向最初分配的内存的指针。根据 documentation 中宏的编写方式,您应该可以通过

访问它

((PyTupleObject *)cpp_attr)->ob_item

而且如果您知道 C++ 指针的数据类型,那么您应该能够将其转换回来。也许你可以试试

MyClass* cpp_att_again = reinterpret_cast<MyClass*>((PyTupleObject *)cpp_attr)->ob_item

希望这能为您指明正确的方向。您或许可以从类似的 question.

中获得更多见解

很难完全确定,但我怀疑 MyClass 是一个 Python 对象。这意味着您尝试将其存储为 Python 对象(例如使用 PyTuple_Pack 是完全错误的,并且会导致 Python 以意想不到的方式发生故障.

将会发生的是 Python 将尝试将指针解释为 Python 对象,将尝试对该对象使用其正常的引用计数机制(将以不可预测的方式更改它) ,并最终尝试释放该对象(使用 Python 机制,而不是删除...)如果对象的某些部分恰好等于 0.

有许多选项,所有选项基本上都围绕创建包装器对象 - 一个用 C++ 定义的 Python 对象,它保存 C++ 对象的指针或值。

  1. 使用 Python C API - .

    手动完成
  2. 查找 the PyCapsule interface 以在您的对象周围创建一个快速包装器。你将创建你的胶囊:

     PyObject* cap = PyCapsule_New(cpp_attr, "MyClass",
           [](PyObject* c) {
               auto deleteme = reinterpret_cast<MyClass*>(PyCapsule_GetPointer(c, "MyClass));
               delete deleteme;
           });
    

    然后您从胶囊中检索您的 C++ class:

     reinterpret_cast<MyClass*>(PyCapsule_GetPointer(c, "MyClass))
    
  3. 使用 PyBind11、Cython、SWIG 等工具为您创建包装器对象。


另请注意,PyObject_SetAttrString 不要求第三个参数是元组(除非您特别想要存储元组...)。您可能会将它与 PyObject_Call 混淆,其中参数作为元组传递。