python以前以其强大的胶水功能著称,当然现在很少这么认为了,因为大多数时候它足够强大,而不需要引入其他语言的特性。在2.5版本的时候引入ctypes库,让混合c/c++模块,大幅提升性能变得非常容易。
这里简单记录一下例子:
//run.cpp
int run(int a, int b)
{
return a + b;
}
int getLength(const char* a, const char* b)
{
return strlen(a) + strlen(b);
}
int mergeStr(const char *a, const char *b, char *buf, int buf_size)
{
return snprintf(buf, buf_size, "%s=>%s", a, b);
}
int sum(int arr[], int n)
{
int res = 0;
for (int i = 0; i < n; ++i) {
res += arr[i];
}
return res;
}
头文件:
//run.h
extern "C" {
int run(int a, int b);
int getLength(const char* a, const char* b);
int mergeStr(const char *a, const char *b, char *buf, int buf_size);
int sum(int arr[], int n);
}
编译成so:
g++ run.cpp -shared -fPIC -o run.so -I./
接着就可以使用了:
from ctypes import *
buf = create_string_buffer(4096)
libs = cdll.LoadLibrary('./run.so')
ret = libs.mergeStr('abc', 'haha', buf, 4096)
print(ret)
print(buf)
print(buf.value)
n = 10
TenIntegers = c_int * n
ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
print(libs.sum(ii), n)
print(sum(ii))
头文件中定义结构体:
//run.h
extern "C" {
struct Point {
int x;
int y;
int z;
};
struct Cube {
Point p1;
Point p2;
};
double distance(Cube *c);
}
简单实现一个算距离的函数:
//run.cpp
double distance(Cube *c)
{
double ret = sqrt(
(c->p1.x - c->p2.x) * (c->p1.x - c->p2.x) +
(c->p1.y - c->p2.y) * (c->p1.y - c->p2.y) +
(c->p1.z - c->p2.z) * (c->p1.z - c->p2.z)
);
printf("%lf\n", ret);
return ret;
}
python里也需要定义结构体的各成员变量类型:
class Point(Structure):
_fields_ = [("x", c_int), ("y", c_int), ("z", c_int)]
class Cube(Structure):
_fields_ = [("p1", Point), ("p2", Point)]
c = Cube(Point(0, 0, 0), Point(1, 1, 1))
libs.distance.restype = c_double
d = libs.distance(byref(c))
print(type(d))
print(d)
注意在调用so里的函数时,会进行参数类型的隐式转换,默认会转成int类型,而返回值默认也会转换成int类型。
设定好argtypes和restype即可,如:
libc = cdll.LoadLibrary('libc.so.6')
libc.strchr.restype = c_char_p
print(libc.strchr('define', ord('f')))
libc.strchr.argtypes = [c_char_p, c_char]
print(libc.strchr('define', 'f'))
跟写C语言一样,需要定义函数原型,使用CFUNCTYPE来定义函数原型,再包装一个python函数,如:
def cmp(a, b):
#注意a和b是指针,所以这里使用a[0]和b[0]
return a[0] - b[0]
CMP_TYPE = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
#相当于int nums[10];
nums = (c_int * 10)(4, 6, 3, 2, 0, 8, 9, 1, 5, 7)
libc.qsort(nums, len(nums), sizeof(c_int), CMP_TYPE(cmp))
print(list(nums))
ctypes这种让python无缝调用C模块函数的强大功能,让C/C++程序员在写python时,大有可为!