简体   繁体   English

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?

[英]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?

I'm making a C++ extension for Python, and I'm trying to do something like:我正在为 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);
}

I would like to retrieve this pointer and set it as attribute of another C++ object.我想检索此指针并将其设置为另一个 C++ object 的属性。 I know how to get the PyObject* but after that I'm not sure what to do anymore我知道如何获取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
    
}

I looked at the documentation but I couldn't find anything that returns the original type.我查看了文档,但找不到任何返回原始类型的内容。 Is there anyway to do this?有没有办法做到这一点?

Thanks谢谢

It's difficult to be absolutely certain, but I doubt that MyClass a Python object.很难绝对确定,但我怀疑MyClass是 Python object。 This means that your attempt to store it as a Python object (eg using PyTuple_Pack ) is completely wrong and will cause Python to malfunction in unexpected ways .这意味着您尝试将其存储为 Python object (例如使用PyTuple_Pack是完全错误的,并且会导致 Python 以意想不到的方式发生故障

What will happen is that Python will attempt to interpret the pointer as a Python object, will try to use its normal reference counting mechanisms on that object (will change it in unpredictable ways), and ultimately try to deallocate that object (using Python mechanisms, not delete...) if some part of the object happens to equal 0. What will happen is that Python will attempt to interpret the pointer as a Python object, will try to use its normal reference counting mechanisms on that object (will change it in unpredictable ways), and ultimately try to deallocate that object (using Python mechanisms,不删除...)如果 object 的某些部分恰好等于 0。

There's a number of options, all basically centred around creating a wrapper object - a Python object defined in C++ that holds either a pointer or value of your C++ object. There's a number of options, all basically centred around creating a wrapper object - a Python object defined in C++ that holds either a pointer or value of your C++ object.

  1. Do it manually using the Python C API - This answer gives a very thorough example .使用 Python C API 手动执行 -这个答案给出了一个非常详尽的例子

  2. Look up the PyCapsule interface to create a quick wrapper around your object.查找PyCapsule 接口以在 object周围创建一个快速包装器。 You'd create your capsule with:您将使用以下内容创建胶囊:

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

    And you retrieve your C++ class from the capsule with:然后从胶囊中检索 C++ class :

     reinterpret_cast<MyClass*>(PyCapsule_GetPointer(c, "MyClass))
  3. Use some tool like PyBind11, Cython, SWIG, etc to create the wrapper object for you.使用 PyBind11、Cython、SWIG 等工具为您创建包装器 object。


Note also that PyObject_SetAttrString does not require the third argument to be a tuple (unless you specifically want to store a tuple...).还要注意PyObject_SetAttrString不需要第三个参数是一个元组(除非你特别想存储一个元组......)。 You're likely getting it confused with PyObject_Call , where the args are passed as a tuple.您可能会将它与PyObject_Call混淆,其中 args 作为元组传递。

Assuming your call to PyTuple_Pack is correct, then you've created a PyTupleObject which has a structure :假设您对PyTuple_Pack的调用是正确的,那么您已经创建了一个PyTupleObject ,它具有以下结构

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

The PyTupleObject inherits from the generic PyObject struct which has the following members: PyTupleObject继承自具有以下成员的通用PyObject struct

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

You can access the latter two with the macros Py_REFCNT andPy_TYPE您可以使用宏Py_REFCNTPy_TYPE访问后两者

The ob_item[1] member should be a pointer to the memory initially allocated. ob_item[1]成员应该是指向最初分配的 memory 的指针。 Based on how Macros are written in the documentation , you should be able to access it by根据文档中宏的编写方式,您应该能够通过以下方式访问它

((PyTupleObject *)cpp_attr)->ob_item

And if you know the data type of the C++ pointer, then you should be able to cast it back.如果您知道 C++ 指针的数据类型,那么您应该可以将其转换回来。 Maybe you can try也许你可以试试

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

Hopefully this points you in the right direction.希望这会为您指明正确的方向。 You might be able to glean more insight from a similar question .您也许可以从类似的问题中获得更多见解。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM