Python 调用C++动态链接库需要extern "C"来辅助,也就是说Python只能调用C,不能直接调用C++.
如:
#include <iostream> using namespace std; class TestLib { public: void display(); void display(int a); }; void TestLib::display() { cout<<"First display"<<endl; } void TestLib::display(int a) { cout<<"Second display:"<<a<<endl; } extern "C" { TestLib obj; void display() { obj.display(); } void display_int() { obj.display(2); } }
C++生成动态链接库:
g++ -o libpycallclass.so -shared -fPIC ./libpycallclass.cpp
Python的简单调用:
import ctypes so = ctypes.cdll.LoadLibrary lib = so("./libpycallclass.so") print 'display()' lib.display() print 'display(100)' lib.display_int(100)
当调用的函数有输入参数
由于Python和C++的数据类型不一致,因此我们需要利用ctypes的工具对两种语言的数据类型进行统一
参考如下表,数据类型的映射关系
ctypes 类型 C 类型 Python 数据类型 c_bool
_Bool
bool (1) c_char
char
单字符字节串对象 c_wchar
wchar_t
单字符字符串 c_byte
char
int c_ubyte
unsigned char
int c_short
short
int c_ushort
unsigned short
int c_int
int
int c_uint
unsigned int
int c_long
long
int c_ulong
unsigned long
int c_longlong
__int64
或long long
int c_ulonglong
unsigned __int64
或unsigned long long
int c_size_t
size_t
int c_ssize_t
ssize_t
或Py_ssize_t
int c_float
float
float c_double
double
float c_longdouble
long double
float c_char_p
char *
(NUL terminated)字节串对象或 None
c_wchar_p
wchar_t *
(NUL terminated)字符串或 None
c_void_p
void *
int 或 None
字符串参数
Python 默认的 string 是不可变的,不能传递 string 到一个 C 函数去改变它的内容,所以需要使用 create_string_buffer,对应 Unicode 字符串,要使用 create_unicode_buffer,下面是创建一个字符数据:
zoon = ctypes.create_string_buffer('49Q')
传递自定义参数类型到 C 函数
ctypes 允许你创建自定义参数类型,它会自动去搜索自定义数据的 _as_parameter 属性,将其作为 C 函数的参数,例如
from ctypes import * libc = cdll.LoadLibrary('libc.so.6') class Bottles(object): def __init__(self, number): self._as_parameter_ = number # here only accept integer, string, unicode string bottles = Bottles(42) libc.printf('%d bottles of beer\n', bottles)
输出:
42 bottles of beer
也可以为你的数据定义 _as_parameter 属性,如下,
from ctypes import * libc = cdll.LoadLibrary('libc.so.6') class Bottles(object): def __init__(self): self._as_parameter_ = None # only accept integer, string, unicode string @property def aspram(self): return self._as_parameter_ @aspram.setter def aspram(self, number): self._as_parameter_ = number bottles = Bottles() bottles.aspram = 63 libc.printf('%d bottles of beer\n', bottles)
输出:
63 bottles of beer
指定 C 函数的参数类型
可以指定要调用 C 函数的参数类型,如果传入参数不符合指定的类型,则 ctypes 会尝试转换,如果转换不成功,则抛 ArgumentError,例如,
from ctypes import * libc = cdll.LoadLibrary('libc.so.6') libc.printf.argtypes = [c_char_p, c_char_p, c_int, c_double] libc.printf('String is "%s", Int is %d, Double is %f\n', 'Hi', 10, 2.2) libc.printf('%s, %d, %f\n', 'X', 2, 3) try: libc.printf("%d %d %d", 1, 2, 3) except ArgumentError, e: print "*** ERROR: %s" % str(e)
输出
String is "Hi", Int is 10, Double is 2.200000 X, 2, 3.000000 *** ERROR: argument 2: <type 'exceptions.TypeError'>: wrong type
指定 C 函数的返回值类型
如果不指定 C 函数的返回值, ctypes 默认返回 int 类型,如果要返回特定类型,需要指定返回类型 restype,
例如,
from ctypes import * libc = cdll.LoadLibrary('libc.so.6') print '1->', libc.strchr('abcdefghij', c_char('d')) #指定返回值类型 libc.strchr.restype = c_char_p print '2->', libc.strchr('abcdefghij', c_char('d')) print '3->', libc.strchr('abcdefghij', 'd') # Note, here C function strchr not know what 'd' mean, so rerurn None #指定输入参数 libc.strchr.argtypes = [c_char_p, c_char] print '4->', libc.strchr('abcdefghij', 'd') # Note, here not use c_char('w')
输出
1-> 40291315 2-> defghij 3-> None 4-> defghij
按引用传递参数
有些情况下,需要 C 函数修改传入的参数,或者参数过大不适合传值,需要按引用传递,ctypes 提供关键字 byref() 处理这种情况,
例如,
from ctypes import * libc = cdll.LoadLibrary('libc.so.6') i = c_int() f = c_float() s = create_string_buffer('\000' * 32) print 'i.val =', i.value print 'f.val =', f.value print 'repr(s.value) =', repr(s.value) libc.sscanf('1 3.14 Hello', '%d %f %s', byref(i), byref(f), s) print 'after, i.val =', i.value print 'after, f.val =', f.value print 'after, repr(s.value) =', repr(s.value)
输出,
i.val = 0 f.val = 0.0 repr(s.value) = '' after, i.val = 1 after, f.val = 3.1400001049 after, repr(s.value) = 'Hello'
使用结构体
ctypes 支持结构体的使用,从 Structure 类派生,数据放在 fields 中,
例如,
class Point(Structure): _fields_ = [('x', c_int), ('y', c_int)] point = Point(10, 20) print 'point.x =', point.x print 'point.y =', point.y point = Point(y=5) print 'after, point.x =', point.x print 'after, point.y =', point.y print class Rect(Structure): _fields_ = [('upperleft', Point), ('lowerright', Point)] rc = Rect(point) print 'rc.upperleft.x = %d, rc.upperleft.y = %d' % (rc.upperleft.x, rc.upperleft.y) print 'rc.lowerright.x = %d, rc.lowerright.y = %d' % (rc.lowerright.x, rc.lowerright.y) r = Rect(Point(1, 2), Point(3, 4)) print 'r.upperleft.x = %d, r.upperleft.y = %d' % (r.upperleft.x, r.upperleft.y) print 'r.lowerright.x = %d, r.lowerright.y = %d' % (r.lowerright.x, r.lowerright.y)
输出,
point.x = 10 point.y = 20 after, point.x = 0 after, point.y = 5 rc.upperleft.x = 0, rc.upperleft.y = 5 rc.lowerright.x = 0, rc.lowerright.y = 0 r.upperleft.x = 1, r.upperleft.y = 2 r.lowerright.x = 3, r.lowerright.y = 4
位域
ctypes 提供了对位域的支持,
例如,
[](javascript:void(0)