Python、C 库和嵌套结构
Python, C library and nested struct
有:
- С 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 - 数组元素的数量。
- 用 С 编写的编译库。
- 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
问题是什么?如何解决?
- 你所有的
POINTER(POINTER(CStruct)
都应该是 POINTER(CBigStruct)
。函数接口 BIG_STRUCT*
而不是 STRUCT**
.
c_struct = CStruct
不会创建实例,而只是为类型提供一个新名称。使用 c_struct = CStruct()
来创建一个实例,但是如果你 return 分配的指针(参见#3),这并不是真正需要的。
struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);
没有意义,应该是struct BIG_STRUCT *allocate_bs()
和return分配的指针。
- 对于像这样的简单示例,为回答的人提供 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)])
有:
- С 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 - 数组元素的数量。
- 用 С 编写的编译库。
- 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
问题是什么?如何解决?
- 你所有的
POINTER(POINTER(CStruct)
都应该是POINTER(CBigStruct)
。函数接口BIG_STRUCT*
而不是STRUCT**
. c_struct = CStruct
不会创建实例,而只是为类型提供一个新名称。使用c_struct = CStruct()
来创建一个实例,但是如果你 return 分配的指针(参见#3),这并不是真正需要的。struct BIG_STRUCT *allocate_bs(struct BIG_STRUCT *bs);
没有意义,应该是struct BIG_STRUCT *allocate_bs()
和return分配的指针。- 对于像这样的简单示例,为回答的人提供 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)])