Python和Ctypes访问带有嵌套结构的C 类

python and ctypes accessing c++ classes with nested structures

本文关键字:结构 嵌套 Ctypes 访问 Python      更新时间:2023-10-16

我有一个共享库和两个标头文件。我能够通过使用swig创建绑定来使用库。但是,swig版本非常慢,我没有设法包括对numpy数组的支持。我现在正在尝试使用ctypes从Python调用库的功能。

第一个标头包含一个extern "C"块,该块通过宏来导出3个函数(我无法使用(。标题的简化版本看起来像这样:

...
class Foo;
typedef Foo* FOO_HANDLE;
#if !defined(__cplusplus)
#   error "C++ Compiler only"
#endif
extern "C"
{
    APIPROTOTYPE_EXT( MakeAlgo, FOO_HANDLE, (void) );
    APIPROTOTYPE_EXT( DestroyAlgo, void, ( FOO_HANDLE ) );
    APIPROTOTYPE_EXT( AlgoProcess, void, ( FOO_HANDLE, StructOne *, StructTwo *) );
}

通常,第一个函数MakeAlgo返回指向Foo类的实例,第二个功能DestroyAlgo破坏了实例,第三个功能AlgoProcess将两个结构作为输入作为输入,并将其一些值定为就位。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

第二个标头包含StructOneStructTwo的定义以及一些常数。StructTwo包含几个嵌套结构。

在Python中,我使用ctypes.Structure重写了第二个标头中包含的所有结构。我不在此处发布所有代码,但这是我定义嵌套结构的示例:

class StructTwo(Structure):
    _fields_ = [("foo", StructFoo),
                ("dummy8", c_short)]
class StructFoo(Structure):
    _fields_ = [("bar", c_uint),
                ("reserv1", c_uint),
                ("reserv2", c_uint)]

然后我的Python代码看起来像这样(假设结构在structures.py文件中(:

from ctypes import *
from structures import StructOne, StructTwo
libfoo = ct.cdll.LoadLibrary(path/to/so/library)
makeAlgo = libfoo.MakeAlgo
makeAlgo.restype = c_void_p
makeAlgo.argtypes = []
destroyAlgo = libfoo.DestroyAlgo
destroyAlgo.restype = None
destroyAlgo.argtypes = [c_void_p]
submit = libfoo.AlgoProcess
submit.restype = None
submit.argtypes = [c_void_p, POINTER(StructOne), POINTER(StructTwo)]
handle = makeAlgo()
one = bar.StructOne()
two = bar.StructTwo()
submit(handle, byref(one), byref(two))
print(two.foo.bar)  # unsigned int, should output 1, got 196611000 instead
destroyAlgo(handle)

创建指向Foo类的指针并提交输入后,我检查了其中一个结构中的一些值,并且与预期结果不符。例如,我知道其中一个字段仅由图书馆设置为0或1,但是我得到了一些奇怪的结果,例如196611000。

有人知道什么问题(也许有人遇到了类似的问题(吗?可以是我定义结构的方式吗?或者也许是指向C 类的指针,该类别未正确处理?

编辑

我设法解决了初始问题。看来我定义结构的方式不正确。代替上述代码,应通过参考传递嵌套结构:

class StructTwo(Structure):
    _fields_ = [("foo", POINTER(StructFoo)),
                ("dummy8", c_short)]
class StructFoo(Structure):
    _fields_ = [("bar", c_uint),
                ("reserv1", c_uint),
                ("reserv2", c_uint)]
# Then to initialize the nested structure
foo = StructFoo()
two = StructTwo(pointer(foo))  # has to be pointer() not byref()

但是,要访问StructFoo的字段,我必须做:

print(two.foo.contents.bar)

在实际代码中,我可能具有多达4个嵌套级别。是否有一种更优雅的方式来访问他们的领域:

two.foo.contents.baz.contents.qux.contents.field_value

我的编辑不正确。

问题在于,某些结构是比特场结构,指定了每个字段的宽度:

class StructTwo(Structure):
    _fields_ = [("foo", StructFoo),
                ("dummy8", c_short)]
class StructFoo(Structure):
    _fields_ = [("bar", c_uint, 1),
                ("reserv1", c_uint, 8),
                ("reserv2", c_uint, 16)]

ctypes中的位野外结构。