提取 SWIG 包装的 C++ instance/pointer 以在 Cython 中使用

Extracting SWIG wrapped C++ instance/pointer for use in Cython

我有一个来自 SWIG 包装的 C++ 库的 class 实例,我想从中提取它的引用,以便能够在我直接在其中的 Cython 文件中使用它通过使用相同 class.

的更轻量级自制 Cython 包装器链接到相同的 C++ 库

我知道这不像访问某些隐藏属性那么容易,但我想如果从 Cython 中链接到 SWIG 或 CPython 中可能有一些函数可能会这样做(也许是一些 PyObject_*?) .

不幸的是,我对 SWIG 或 CPython 的内部知识了解不够,不知道如何做到这一点,或者这是否真的可行而不修改 SWIG 绑定的源代码。

经过进一步研究,我想出了如何做我想做的事。

根据其 .this 属性中包含的 the documentation, SWIG-wrapped classes in Python consist of three layers: a) a pure Python instance, b) a custom SwigPyObject built-in type 和 c) 通常无法访问的指针 void *ptr 指向其中包含的实际 C++ 实例,这正是 Cython 所需要的。

为了访问该指针,我必须创建 SwigPyObject 结构的 Cython 包装器,以便访问 SWIG 实例的内部 void *ptr。此结构的声明通常直接包含在 SWIG-generated C++ 源代码中,而不是作为单独的 header,因此我创建了一个包含它的文件:

#include <Python.h>

typedef struct {
  PyObject_HEAD
  void *ptr; // This is the pointer to the actual C++ instance
  void *ty;  // swig_type_info originally, but shouldn't matter
  int own;
  PyObject *next;
} SwigPyObject;

然后在 Cython .pxd 文件中引用此包含文件,以启用对内部 ptr 变量的访问:

cdef extern from "swigpyobject.h":
    ctypedef struct SwigPyObject:
        void *ptr

现在可以从 .pyx Cython 源代码中引用所需的指针:

 cdef SwigPyObject *swig_obj = <SwigPyObject*>pythonswig.this
 cdef MyCppClass *mycpp_ptr = <MyCppClass*?>swig_obj.ptr
 // And if you want a proper instance instead of a pointer:
 cdef MyCppClass my_instance = deref(mycpp_ptr)

警告:由于 SWIG-wrapped 类 存在于 Python 和 C++ space 中,SWIG 实现了处理内存分配的机制,包括有关 [=垃圾实例的 32=] collection。此代码不会尝试处理任何这些,因此可能存在 运行 分配问题的危险,但只要在 Python 中保留对原始 SWIG 实例的引用,我相信在 Cython 下操作它应该是安全的。