python 和 ctypes 使用嵌套结构访问 c++ 类
python and ctypes accessing c++ classes with nested structures
我有一个共享库和两个 header 文件。我能够通过使用 swig
创建绑定来使用该库。但是,swig
版本非常慢,我没有设法包含对 numpy
数组的支持。我现在正尝试使用 ctypes
从 Python 调用库的函数。
第一个 header 包含一个 extern "C"
块,它通过宏导出 3 个函数(我无法使用)。 header 的简化版本如下所示:
...
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
returns一个指向Foo
实例的指针class,第二个函数DestroyAlgo
销毁实例,第三个函数 AlgoProcess
将两个结构作为输入并修改它们的一些值 in-place.
第二个 header 包含 StructOne
和 StructTwo
的定义以及一些常量。 StructTwo
包含多个嵌套结构。
在Python中,我使用ctypes.Structure
重写了第二个header中包含的所有结构。我没有 post 这里的所有代码,但这是我如何定义嵌套结构的示例:
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
class 的指针后,提交输入后,我检查了其中一个结构中的某些值,发现它与预期结果不匹配。例如,我知道图书馆仅将其中一个字段设置为 0 或 1,但我得到了一些奇怪的结果,例如 196611000.
有谁知道哪里出了问题(也许有人遇到过类似的问题)?可以是我定义结构的方式吗?或者它可能是指向未正确处理的 C++ class 的指针?
编辑
我设法解决了最初的问题。看来我定义结构的方式不正确。而不是上面的代码,嵌套结构应该通过引用传递:
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)]
我有一个共享库和两个 header 文件。我能够通过使用 swig
创建绑定来使用该库。但是,swig
版本非常慢,我没有设法包含对 numpy
数组的支持。我现在正尝试使用 ctypes
从 Python 调用库的函数。
第一个 header 包含一个 extern "C"
块,它通过宏导出 3 个函数(我无法使用)。 header 的简化版本如下所示:
...
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
returns一个指向Foo
实例的指针class,第二个函数DestroyAlgo
销毁实例,第三个函数 AlgoProcess
将两个结构作为输入并修改它们的一些值 in-place.
第二个 header 包含 StructOne
和 StructTwo
的定义以及一些常量。 StructTwo
包含多个嵌套结构。
在Python中,我使用ctypes.Structure
重写了第二个header中包含的所有结构。我没有 post 这里的所有代码,但这是我如何定义嵌套结构的示例:
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
class 的指针后,提交输入后,我检查了其中一个结构中的某些值,发现它与预期结果不匹配。例如,我知道图书馆仅将其中一个字段设置为 0 或 1,但我得到了一些奇怪的结果,例如 196611000.
有谁知道哪里出了问题(也许有人遇到过类似的问题)?可以是我定义结构的方式吗?或者它可能是指向未正确处理的 C++ class 的指针?
编辑
我设法解决了最初的问题。看来我定义结构的方式不正确。而不是上面的代码,嵌套结构应该通过引用传递:
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)]