Python、C 库和嵌套结构

Python, C library and nested struct

有:

  1. С header 结构
struct STRUCT {
    float x;
    float y;
    float z;
};

struct BIG_STRUCT {
    int value;
    int array_count;
    struct STRUCT *array;
};

struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);
struct BIG_STRUCT *do_something_bs(struct BIG_STRUCT *bs);
void free_bs(struct BIG_STRUCT *bs);

array_count - 数组元素的数量。

  1. 用 С 编写的编译库。
  2. Python 使用 ctypes 的文件。
import ctypes
from ctypes import *

class CStruct(ctypes.Structure):
    _fields_ = [
        ('x', ctypes.c_float),
        ('y', ctypes.c_float),
        ('z', ctypes.c_float)
    ]

class CBigStruct(ctypes.Structure):
    _fields_ = [
        ('value', ctypes.c_int),
        ('array_count', ctypes.c_int),
        ('array', ctypes.POINTER(ctypes.POINTER(CStruct)))
    ]

if __name__ == '__main__':
    libc = ctypes.CDLL("./library/libTestPython.so.0.0")

    c_struct = CStruct

    libc.allocate_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
    libc.allocate_bs.restype = ctypes.POINTER(ctypes.POINTER(CStruct))
    result = libc.allocate_bs(ctypes.byref(c_struct))

    libc.do_something_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
    libc.do_something_bs.restype = ctypes.POINTER(ctypes.POINTER(CStruct))
    result = libc.do_something_bs(c_struct)

    libc.free_bs.argtypes = [ctypes.POINTER(ctypes.POINTER(CStruct))]
    libc.free_bs.restype = ctypes.c_int
    libc.free_bs(c_struct)

错误

Traceback (most recent call last):
  File "./example_struct.py", line 39, in <module>
    result = libc.allocate_bs(ctypes.byref(c_struct))
TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCStructType'

字符串结果中没有 ctypes.bref() = libc.allocate_bs(c_struct) 我有一个错误

Traceback (most recent call last):
  File "./example_struct.py", line 39, in <module>
    result = libc.allocate_bs(c_struct)
ctypes.ArgumentError: argument 1: <class 'TypeError'>: expected LP_LP_CStruct instance instead of _ctypes.PyCStructType

问题是什么?如何解决?

  1. 你所有的 POINTER(POINTER(CStruct) 都应该是 POINTER(CBigStruct)。函数接口 BIG_STRUCT* 而不是 STRUCT**.
  2. c_struct = CStruct 不会创建实例,而只是为类型提供一个新名称。使用 c_struct = CStruct() 来创建一个实例,但是如果你 return 分配的指针(参见#3),这并不是真正需要的。
  3. struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);没有意义,应该是struct BIG_STRUCT *allocate_bs()和return分配的指针。
  4. 对于像这样的简单示例,为回答的人提供 C 代码的最小实现,以证明他们的答案并明确您希望代码做什么。对我来说工作更少

这是有用的东西:

// test.c -> test.dll
// Compiled with Microsoft compiler: cl /LD /W4 test.c
#include <stdlib.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

struct STRUCT {
    float x;
    float y;
    float z;
};

struct BIG_STRUCT {
    int value;
    int array_count;
    struct STRUCT *array;
};

// Allocate and return an intialized structure
API struct BIG_STRUCT *allocate_bs() {
    struct BIG_STRUCT* bs = malloc(sizeof(struct BIG_STRUCT));
    bs->value = 0;
    bs->array_count = 0;
    bs->array = NULL;
    return bs;
}

// fill out the structure with an allocated array and some data
API void do_something_bs(struct BIG_STRUCT *bs) {
    bs->value = 7;
    bs->array_count = 2;
    bs->array = malloc(2 * sizeof(struct STRUCT));
    for(int i = 0; i < 2; ++i) {
        bs->array[i].x = (float)i;
        bs->array[i].y = (float)(i+1);
        bs->array[i].z = (float)(i+2);
    }
}

API void free_bs(struct BIG_STRUCT *bs) {
    if(bs != NULL) {
        if(bs->array != NULL)
            free(bs->array);
        free(bs);
    }
}
# test.py
import ctypes as ct

class CStruct(ct.Structure):

    _fields_ = (('x', ct.c_float),
                ('y', ct.c_float),
                ('z', ct.c_float))

    # So the structure can display itself when printed
    def __repr__(self):
        return f'CStruct(x={self.x}, y={self.y}, z={self.z})'

class CBigStruct(ct.Structure):

    _fields_ = (('value', ct.c_int),
                ('array_count', ct.c_int),
                ('array', ct.POINTER(CStruct)))

    # slicing the pointer with the correct length will return a Python list
    def __repr__(self):
        return f'CBigStruct(value={self.value}, array={self.array[:self.array_count]})'

libc = ct.CDLL('./test')
libc.allocate_bs.argtypes = ()
libc.allocate_bs.restype = ct.POINTER(CBigStruct)
libc.do_something_bs.argtypes = ct.POINTER(CBigStruct),
libc.do_something_bs.restype = None
libc.free_bs.argtypes = ct.POINTER(CBigStruct),
libc.free_bs.restype = None

bs = libc.allocate_bs()
print(bs.contents)
libc.do_something_bs(bs)
print(bs.contents)
libc.free_bs(bs)

输出:

CBigStruct(value=0, array=[])
CBigStruct(value=7, array=[CStruct(x=0.0, y=1.0, z=2.0), CStruct(x=1.0, y=2.0, z=3.0)])