我有一个Python C扩展程序,用于包装专有产品的库。我们公司有大量使用专有产品的C代码。不用使用C扩展在Python中重写它,我想我可以简单地将Capsule返回到Python领域,并允许我的库用户使用ctypes包装一些C函数。
这是有效的方法吗?有更好的吗?
这是一些代码来说明我的方法。
我的Python C扩展:
typedef struct {
PyObject_HEAD
Foo *foo; /* The proprietary data structure we are wrapping */
} PyFoo;
/*
* Expose a pointer to Foo such that ctypes can use it
*/
static PyObject PyFoo_capsule(PyFoo *self, PyObject *args, PyObject *kwargs)
{
return PyCapsule_New(self->foo, "foo", NULL);
}
这是我们团队编写的一些已有的C代码,想从Python调用:
void print_foo(Foo *foo)
{
Foo_print(foo);
}
在Python中,我们可以使用ctypes包装第三方C代码(我在这里学到了):
import pyfoo
import ctypes
foo = pyfoo.Foo()
capsule = foo.capsule()
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [ctypes.py_object, ctypes.c_char_p]
pointer = ctypes.pythonapi.PyCapsule_GetPointer(
capsule,
ctypes.create_string_buffer("foo".encode())
)
libfoo = ctypes.CDLL('libfoo.so')
libfoo.print_foo.restype = None
libfoo.print_foo.argtypes = [ctypes.POINTER(None)]
libfoo.print_foo(pointer)
它将起作用,但是我不喜欢void*
对不透明类型使用s的方法,因为任何方法void*
都可以,但是在C方面,类型很重要,并且如果指向该类型的指针,您的诊断很可能是段错误(或更糟糕的是)传递了错误的类型。
大多数(自动)联编程序(SWIG,pybind11,C / C ++的cppyy或C的CFFI)都会为不透明的C / C ++生成Python类型,以允许类型匹配。
这是一个cppyy(http://cppyy.org)示例,假设文件foo.h
如下:
struct Foo;
struct Bar;
typedef Foo* FOOHANDLE;
typedef Bar* BARHANDLE;
void use_foo(FOOHANDLE);
void use_bar(BARHANDLE);
和一些匹配的库libfoo.so
,然后从cppyy中使用时,您只能通过FOOHANDLE参数等传递FOOHANDLE,以便获得干净的Python端回溯,而不是C端崩溃。会话示例:
>>> import cppyy
>>> cppyy.c_include("foo.h") # assumes C, otherwise use 'include'
>>> cppyy.load_library("libfoo")
>>> foo = cppyy.gbl.FOOHANDLE() # nullptr; can also take an address
>>> cppyy.gbl.use_foo(foo) # works fine
>>> cppyy.gbl.use_bar(foo)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: void ::use_bar(Bar*) =>
TypeError: could not convert argument 1
>>>
编辑:对于一些工作,同样可以用ctypes的,如下所示完成的,因此如果你揭露一个C函数返回self->foo
一个Foo*
你同样可以标注其restype
使用Python FOOHANDLE
,从而绕过胶囊和其余类型的安全:
import ctypes
libfoo = ctypes.CDLL('./libfoo.so')
class Foo(ctypes.Structure):
_fields_ = []
FOOHANDLE = ctypes.POINTER(Foo)
class Bar(ctypes.Structure):
_fields_ = []
BARHANDLE = ctypes.POINTER(Bar)
libfoo.use_foo.restype = None
libfoo.use_foo.argtypes = [FOOHANDLE]
libfoo.use_bar.restype = None
libfoo.use_bar.argtypes = [BARHANDLE]
foo = FOOHANDLE()
libfoo.use_foo(foo) # succeeds
libfoo.use_bar(foo) # proper python TypeError
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句