SWIG包装了从python过早删除创建的C ++对象

时间:2013-03-25 22:00:22

标签: python swig

我使用SWIG在python中包装了一个C ++类和一个C ++函数。

class Module { ... };
void register_module(Module *m);

函数register_module()将给定模块放在一个全局列表中,以便从那时起C ++代码在模块上保存一个指针并使用它。

现在,以下python代码从嵌入式python解释器运行,崩溃

>>> register_module(Module())

另一方面,以下python代码不会崩溃

>>> m = Module()
>>> register_module(m)

显然,原因是在第一种情况下,对象是由python收集的垃圾,而在第二种情况下则不是。

阻止python删除在第一种情况下创建并由C ++代码使用的匿名对象的最佳方法是什么?

在register_module()函数中,是否有办法获取相关python代理对象的保留,然后增加其引用计数?

(我知道如果有问题的类可以交叉转换为Swig :: Director类,但是假设我们有一个在python中定义的Module子类的实例,这不是案例)。

1 个答案:

答案 0 :(得分:1)

解。
第1步:在Module类中添加一个成员,以及两种方法。

class Module {
public:
    PyObject *obj;
    void incref() { Py_INCREF(obj); }
    void decref() { Py_DECREF(obj); }
    ...
};

第2步:增加register_module()中的引用计数。

void register_module(Module *m) {
    m->incref();
    ...
}

(别忘了把它减少到某个地方)

第3步:每当从Python构造Module对象时,破解SWIG生成的包装器以将obj成员设置为PyObject包装器。

在SWIG生成的文件ModulePYTHON_wrap.cxx中,找到:

SWIGINTERN PyObject *_wrap_new_Module(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
  PyObject *resultobj = 0;
  ...
  Module *result = 0 ;
  ...
  if ( arg1 != Py_None ) {
    /* subclassed */
    result = (Module *)new SwigDirector_Module(arg1);
  } else {
    result = (Module *)new Module();
  }

  resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Module, SWIG_POINTER_NEW |  0 );
  return resultobj;
  ...
}

result->obj =之前插入resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Module, SWIG_POINTER_NEW | 0 ); 我们获得:

  result->obj = resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Module, SWIG_POINTER_NEW |  0 );

第4步:指示SWIG在生成的包装器中自动实现修复。

在接口文件中添加以下typemap声明。

%typemap(out) Module* {
   result->obj = $result = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Module, SWIG_POINTER_NEW |  0 );
}

这有效地覆盖了默认生成的用于包装新Module对象的代码。