Python与SWIG封装类型的回调

2 投票
2 回答
2353 浏览
提问于 2025-04-15 15:06

我正在尝试将一个Python的回调函数添加到一个C++库中,像这样:

template<typename T> void doCallback(shared_ptr<T> data) {
   PyObject* pyfunc; //I have this already
   PyObject* args = Py_BuildValue("(O)", data);
   PyEval_CallObject(pyfunc,args);
}

但是这样做失败了,因为数据没有经过swig处理,所以它不是一个PyObject。

我尝试使用:

swigData = SWIG_NewPointerObj((void*)data, NULL, 0);

但因为这是一个模板,我不太清楚第二个参数应该用什么。即使我硬编码了“正确”的SWIGTYPE,通常在调用PyEval_CallObject时也会出现段错误。

所以我有几个问题:

  1. 调用swig类型包装的最佳方法是什么?

  2. 我这样做方向对吗?Directors看起来很有希望可以实现回调,但我找不到关于directors和Python的例子。

更新:正确的包装已经生成。我还有其他函数返回shared_ptr,并且可以正确调用这些函数。

2 个回答

0

shared_ptr<T> 这个东西对于未知的 T 来说并不是一个具体的类型,所以 SWIG 无法处理它。你需要做的是为每一个你想用的 shared_ptr 提供一个 SWIG 的包装。举个例子,如果你想用 doCallback() 来处理 shared_ptr<Foo>shared_ptr<Bar>,你就需要:

  • Foo 创建一个包装
  • Bar 创建一个包装
  • shared_ptr<Foo>shared_ptr<Bar> 创建包装。

你可以这样来创建这些包装:

namespace boost {
    template<class T> class shared_ptr
      {
      public:
          T * operator-> () const;
      };
  }

%template(FooSharedPtr) boost::shared_ptr<Foo>;
%template(BarSharedPtr) boost::shared_ptr<Bar>;
0

我之前的回答完全误解了问题,所以我们再试一次。

你主要的问题在于doCallback定义中的自由类型参数T。正如你在问题中提到的,没有一个具体的T值,就无法从shared_ptr<T>创建一个SWIG对象:shared_ptr<T>实际上并不是一个具体的类型。

因此,我认为你需要进行特化:对于主机系统使用的每一个具体的doCallback实例,都要为目标类型提供一个模板特化。这样一来,你就可以生成一个适合Python使用的data包装,并将其传递给你的Python函数。实现这一点最简单的方法可能是:

swigData = SWIG_NewPointerObj((void*)(data.get()), SWIGType_Whatever, 0);

...不过这只有在你的Python函数不在任何地方保存它的参数时才能工作,因为shared_ptr本身并不会被复制。

如果你确实需要保留对data的引用,你就需要使用SWIG通常用来包装shared_ptr的机制。如果没有特殊的智能指针处理,可能会是这样的:

pythonData = new shared_ptr<Whatever>(data);
swigData = SWIG_NewPointerObj(pythonData, SWIGType_shared_ptr_to_Whatever, 1);

无论如何,这样你就得到了一个适合Python使用的SWIG对象,可以方便地使用Py_BuildValue()

希望这能帮到你。

撰写回答