python 和 ctypes 访问具有嵌套结构的 c++ 类

Posted

技术标签:

【中文标题】python 和 ctypes 访问具有嵌套结构的 c++ 类【英文标题】:python and ctypes accessing c++ classes with nested structures 【发布时间】:2018-04-25 10:37:06 【问题描述】:

我有一个共享库和两个头文件。我可以通过使用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

【问题讨论】:

如果从 C (C++) 应用程序调用它是否有效? 总的来说,它看起来是正确的,但是结构定义可能是错误的,并且没有提供 C 定义。 @CristiFati,是的,C++ 版本按预期工作,swig 包装器也是如此。 【参考方案1】:

我的编辑不正确。

问题是有些结构是位域结构,指定每个域的宽度就解决了:

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)]

Bit-field structures in ctypes.

【讨论】:

以上是关于python 和 ctypes 访问具有嵌套结构的 c++ 类的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 Python 和 ctype 访问 C 全局变量结构

python ctypes:从指针变量中获取指向类型

如何管理 ctypes 中的嵌套结构?

Python & Ctypes:将结构作为指针传递给函数以获取数据

使用 ctypes 的 .dll 中的 python 中的 C++ 函数 - 未找到函数并且访问冲突

用于字节对齐读取的python ctypes pragma pack