让Python与C/C++协同工作 (1)

用Python生成C/C++的数据
Python语言简洁方便易于开发,用来快速的生成和处理数据最好不过了,所以用Python为C/C++生成测试数据在好不过了。但是由于两者在内部的数据存储方面有很大的不同,比如Python的内建类型的float就比C/C++的double要精确的多,所以如果要为C/C++生成二进制的数据(比如通过socket直接发送包含double数据的帧),应该怎么做呢?

其实Python的内建模块中,就至少有这两个是与C/C++联系十分紧密的:

  • ctype[A foreign function library for Python]
  • struct[Interpret strings as packed binary data]如果仅仅是生成数据,而不是想调用C/C++的那一票函数的话(话说有Python在手,谁还想用那些难用的C/C++函数),用struct模块要更加方便一些。

下面提供一个例子,比如想要生成一个RFC 959[File Transfer Protocol]中的某帧格式如下所示的帧

Byte string:

             1       7                8                     8
            +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+
            |0|       n     | |    d(1)       | ... |      d(n)     |
            +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+     +-+-+-+-+-+-+-+-+
                                          ^             ^
                                          |---n bytes---|
                                              of data
01 # 用于C语言数据封装
02 from struct import *
03 ...
04 # 将要传输的数据,考虑到上面的帧格式,长度定义得比较短...
05 data = "Hello World!"
06 # 为将要传输的数据准备一个buffer
07 buff = ""
08 # 开始封装
09 buff += pack('!c', chr(len(data)))
10 buff += pack('!%ds'%len(data), data)
11 # 封装完毕,直接发出去就可以了 
在这个代码片段中,核心就是pack函数。而pack函数的核心是格式化字符串,这个东西和printf是差不多的。具体参数说明(引自[Python官方文档](http://docs.python.org/library/struct.html)):
Format C Type Python Notes
x pad byte no value
c char string of length 1
b signed char integer
B unsigned char integer
? _Bool bool (1)
h short integer
H unsigned short integer
i int integer
I unsigned int integer or long
l long integer
L unsigned long long
q long long long (2)
Q unsigned long long long (2)
f float float
d double float
s char[] string
p char[] string
P void * long
Notes:

1.The ‘?’ conversion code corresponds to the _Bool type defined by C99. If this type is not available, it is simulated using a char. In standard mode, it is always represented by one byte.

New in version 2.6.

2.The ‘q’ and ‘Q’ conversion codes are available in native mode only if the platform C compiler supports C long long, or, on Windows, __int64. They are always available in standard modes.

New in version 2.2.

貌似字符串部分是可以用p取代繁琐的ns的(n表示的是字节数量,在上文中是len(data))。

然后一个需要关心的就是网络字节序的问题,这个也就是上文中pack函数中’!’字段的作用了(引自Python官方文档):

Character Byte order Size and alignment
@ native native
= native standard
< little-endian standard
> big-endian standard
! network (= big-endian) standard
If the first character is not one of these, '@' is assumed.

当然如果仅仅是本机调试的话,要注意的问题会少很多,比如字节序的问题就可以忽略。下面再提供一个double型pack的例子:

01 # 用于C语言数据封装
02 from struct import *
03 ...
04 # 对一个时序列来一次fft
05 s = [1, 2, 3, 4, 5, 6, 7, 8]
06 so = fft(s)
07 # 为将要传输的数据准备一个buffer
08 buff = ""
09 # 开始封装
10 for d in so:
11     buff += pack('@dd', d.real, d.imag)
12 # 封装完毕,直接发出去就可以了 
很简单!