在阅读this和this之后,我尝试用C编写自己的“共享库”,并尝试使用Python包装器来尝试理解ctypes。
请注意,我故意在Python包装器中注释了成员a
,以探讨当我在C结构的成员与Python类_fields_
之间的映射不完全一对一时会发生什么情况:
testlib.c :
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void myprint();
void myprint() {
printf("Hello world\n");
}
typedef struct object_t {
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
uint8_t e;
uint32_t f;
uint8_t* g;
} object_t;
static object_t object = {'a', 'b', 'c', 'd', 'e', 12345, NULL};
object_t* func1() {
return &object;
}
void func2(void(*callback)(object_t*), object_t* this_object) {
callback(this_object);
}
我将testlib.c
编译到共享库testlib.so
的同一文件夹中,如下所示:
gcc -shared -o testlib.so -fPIC testlib.c
wrapper.py :
from ctypes import *
testlib = CDLL('./testlib.so')
testlib.myprint()
class object_t(Structure):
_fields_ = [
# ('a', c_uint8),
('b', c_uint8),
('c', c_uint8),
('d', c_uint8),
('e', c_uint8),
('f', c_uint32),
('g', POINTER(c_uint8)),
]
callback_t = CFUNCTYPE(None, POINTER(object_t))
func1 = testlib.func1
func1.argtypes = None
func1.restype = POINTER(object_t)
func2 = testlib.func2
func2.argtypes = [callback_t]
func2.restype = None
ret = func1()
# a = ret.contents.a
b = ret.contents.b
c = ret.contents.c
d = ret.contents.d
e = ret.contents.e
f = ret.contents.f
g = ret.contents.g
print("{} {} {} {} {} {}".format(
# chr(a),
chr(b),
chr(c),
chr(d),
chr(e),
chr(f),
g,
))
def mycallback(obj):
# a = obj.contents.a
b = obj.contents.b
c = obj.contents.c
d = obj.contents.d
e = obj.contents.e
f = obj.contents.f
g = obj.contents.g
print("{} {} {} {} {} {}".format(
# chr(a),
chr(b),
chr(c),
chr(d),
chr(e),
chr(f),
g,
))
func2(callback_t(mycallback), ret)
当我运行python wrapper.py
时,得到以下输出:
Hello world
a b c d e <__main__.LP_c_ubyte object at 0x7f503901cd08>
a b c d e <__main__.LP_c_ubyte object at 0x7f503902c048>
我看到这并没有失败;相反,我仍然得到前五个字母,然后是指针对象。
该映射是否仅对应于每个成员类型应该占用的内存,然后将该内存推入我在Python字段中指定的类型,而不管它实际上是否有意义? (即我在C端的“ 12345”被放到Python端的POINTER(c_uint8)中)
我问的原因是因为我正在查看Kazam(https://bazaar.launchpad.net/~kazam-team/kazam/stable/view/head:/kazam/pulseaudio/ctypes_pulseaudio.py)中PulseAudio的ctypes绑定,并且发现pa_source_info._fields_
中的许多成员都已被注释掉(第119-135行) )。
我的猜测是,如果我想取消注释其他项,除非连续地取消注释列表中的ctypes,否则ctypes将无法正确处理该映射,然后为当前未使用的类型映射添加类/类型,例如{{1 }}或pa_usec_t
。有人可以确认吗?谢谢。
答案 0 :(得分:2)
我的猜测是,如果我想取消注释其他项目,除非连续地取消注释列表中的ctypes,否则ctypes将无法正确处理该映射,然后为当前未使用的类型映射添加类/类型,例如{{1 }}或
pa_usec_t
。
是的,确实如此。 pa_source_port_info
试图完全遵循C编译器的行为(但存在一些位域错误)。存储器中的ctypes
仅是struct s
个连续字节。为了使ctype产生正确的布局,所有成员都必须至少具有相同的大小,并且与C结构中的相应成员具有相同的对齐要求。
在this question中讨论了C结构的布局和内部填充。
答案 1 :(得分:0)
这甚至比您想象的还要糟糕:如果字节不完全重叠,则值的 part 可能会显示为下一个成员。并且填充可以很容易地预测出未对齐的内容:这里,e
之后可能有三个字节,很有可能为0。删除a
也删除了填充,所以Python中的f
与C语言中的e
对齐。