python强大的ctypes

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模块?

跟写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时,大有可为!

发表于 2017年03月27日 15:19   评论:0   阅读:3194  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo