Python与SWIG封装类型的回调
我正在尝试将一个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时也会出现段错误。
所以我有几个问题:
调用swig类型包装的最佳方法是什么?
我这样做方向对吗?Directors看起来很有希望可以实现回调,但我找不到关于directors和Python的例子。
更新:正确的包装已经生成。我还有其他函数返回shared_ptr,并且可以正确调用这些函数。
2 个回答
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>;
我之前的回答完全误解了问题,所以我们再试一次。
你主要的问题在于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()
。
希望这能帮到你。