boost::python:如何调用一个需要指针的函数?
我有一个函数,它接收一个整型指针,并通过boost::python暴露出来。我该如何在Python中调用这个函数呢?
在C++中使用boost::python的代码如下:
void foo(int* i);
...
def("foo", foo);
在Python中的调用方式:
import foo_ext
i = 12
foo_ext.foo(i)
结果是:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
foo(int)
did not match C++ signature:
foo(int* i)
那么,如何传递一个指针呢?
3 个回答
在大多数情况下,你可以避免将原始指针传递给函数,但是当确实需要这样做时,你可以通过适配器创建一个Python对象来表示C++指向原始对象的指针,方法如下:
template<typename PtrT>
struct PtrAdapter {
auto& get(PtrT ptr) { return *ptr; }
};
接着,定义指针类型到Python对象的映射,并允许隐式转换:
class_<Cluster<LinksT>*, noncopyable>(typpedName<LinksT>("ClusterPtr", true, true)
, "Raw hierarchy cluster pointer\n")
.def("__call__", &PtrAdapter<Cluster<LinksT>*>::get,
return_internal_reference<>(),
"referenced cluster")
;
register_ptr_to_python<Cluster<LinksT>*>();
注意,原始对象类型也应该有对应的Python对象映射(在这个例子中是Cluster<LinksT>
)。
然后,对于这样的C++代码:
Cluster<LinksT>* cl = clusters.head();
process(cl);
Id cid = cl->id();
你可以使用类似的Python代码:
cl = clusters.head()
process(cl)
cid = cl.id()
来自 python.org 的 boost.python 使用指南
也许你希望生成的 Python 对象包含一个指向参数的原始指针?在这种情况下,需要注意的是,如果 C++ 对象的生命周期结束得比 Python 对象早,那么这个指针就会变得无效,使用这个 Python 对象可能会导致程序崩溃。
下面是如何在模块初始化时暴露可变的 C++ 对象:
scope().attr("a") = object(ptr(&class_instance));
简单来说:你不能。Python没有指针。
详细说说:根据不同的情况,有一些变通的方法。
我注意到你在例子中使用了一个整数和一个整数指针。整数(还有浮点数、字符串和布尔值)在Python中是个特殊情况,因为它是不可变的。
假设你传入的对象其实不是一个整数。
你可以写一个包装函数,把参数作为引用传入,获取地址并传递给实际的函数。在Python中这样做是没问题的。
好吧,假设它真的是一个整数。现在你就遇到问题了。你不能改变传入的整数。如果你尝试同样的解决方案,boost::python会在运行时抱怨说有问题。还有几种选择。
假设你不需要在函数结束后查看整数的样子,并且你知道函数不会在返回后保留指针:
你的包装函数现在应该按值或按常量引用接收整数。其他的都一样。
也许你只需要查看函数执行后的状态(整数是一个输出参数):
你的包装函数现在不需要任何参数,而是将一个本地整数的地址传递给实际的函数。它会返回这个值。如果你的函数已经有返回值,它现在应该返回一个元组。
输入和输出都很重要,并且你知道函数不会在返回后保留指针:
把上面的两种情况结合起来。包装函数接收一个整数作为输入,并返回一个不同的整数。
函数期望在返回后保留指针:
没有什么好的解决方案。你可以在C++中创建并暴露一个包含C++整数的对象。包装函数将通过引用接收这个对象,提取里面整数的地址并传递给实际的函数。确保这个对象在Python中保持活跃(并且不被垃圾回收)直到库用完它,这就成了Python开发者的问题,如果处理不当,数据可能会损坏或者解释器崩溃。