代码之家  ›  专栏  ›  技术社区  ›  mfedoten

python和ctypes使用嵌套结构访问c++类

  •  0
  • mfedoten  · 技术社区  · 7 年前

    我有一个共享库和两个头文件。我可以通过使用创建绑定来使用库 swig .然而 swig公司 版本非常慢,我没有设法包括对的支持 numpy 阵列。我现在尝试使用 ctypes

    第一个标题包含 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 将两个结构作为输入,并在位修改它们的一些值。

    第二个标题包含 StructOne 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
    

    1 回复  |  直到 7 年前
        1
  •  1
  •   mfedoten    7 年前

    我的编辑不正确。

    问题是一些结构是位域结构,指定每个域的宽度可以解决它:

    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