Python调用C++

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_Boolbool (1)
c_charchar单字符字节串对象
c_wcharwchar_t单字符字符串
c_bytecharint
c_ubyteunsigned charint
c_shortshortint
c_ushortunsigned shortint
c_intintint
c_uintunsigned intint
c_longlongint
c_ulongunsigned longint
c_longlong__int64long longint
c_ulonglongunsigned __int64unsigned long longint
c_size_tsize_tint
c_ssize_tssize_tPy_ssize_tint
c_floatfloatfloat
c_doubledoublefloat
c_longdoublelong doublefloat
c_char_pchar * (NUL terminated)字节串对象或 None
c_wchar_pwchar_t * (NUL terminated)字符串或 None
c_void_pvoid *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)😉

class IntBit(Structure):
   _fields_ = [('x', c_uint, 2), ('y', c_uint, 4)]
   
IB = IntBit(1, 15)
print 'IB.x = %d' % IB.x 
print 'IB.y = %d' % IB.y
   
IB2 = IntBit(4, 16)
print '-> IB2.x = %d' % IB2.x 
print '-> IB2.y = %d' % IB2.y

输出,

IB.x = 1
IB.y = 15
-> IB2.x = 0
-> IB2.y = 0

数组

ctypes 提供了对 Array 的支持,

例如,

TenIntArrayType = c_int * 10
ta = TenIntArrayType(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for item in ta:
   print item,
print
   
class PointEx(Structure):
   _fields_ = [('x', c_int), ('y', c_int)]
       
class MyStruct(Structure):
   _fields_ = [('a', c_int), ('b', c_int), ('pointex_array', PointEx * 4)]
       
ms = MyStruct(4, 5, ((1,1), (2,2), (3,3), (4,4)))
for item in ms.pointex_array:
   print '(item.x, item.y) = (%d, %d)' % (item.x, item.y) 
print

输出,

1 2 3 4 5 6 7 8 9 10
(item.x, item.y) = (1, 1)
(item.x, item.y) = (2, 2)
(item.x, item.y) = (3, 3)
(item.x, item.y) = (4, 4)

指针

ctypes 使用关键字 pointer 提供了对指针的支持,注意指针解引用使用 [0],

例如,

i = c_int(42)
print 'before, i.value =', i.value
pi = pointer(i)
pi[0] = 57 # 类比C中的*pi
print 'after, i.value =', i.value
   
# create NULL pointer, also can use this way, but recommend use 'pointer' not 'POINTER'
null_ptr = POINTER(c_int)()
print 'bool(null_ptr) =', bool(null_ptr)

输出,

before, i.value = 42
after, i.value = 57
bool(null_ptr) = False

类型转换

ctypes 提供了类型转换方法 cast(),

例如,

class Bar(Structure):
   _fields_ = [('count', c_int), ('value', POINTER(c_int))]
       
bar = Bar()
bar.count = 3
bar.value = (c_int * 3)(1, 2, 3)
for idx in range(bar.count):
   print 'bar.value[%d] = %d' % (idx, bar.value[idx])
   
## use cast to convert 
try:
   bar.value = (c_byte * 4)()
except TypeError, e:
   print '*** ERROR: %s' % str(e)
       
bar.value = cast((c_byte * 4)(), POINTER(c_int))
for idx in range(4):
   print 'now, bar.value[%d] = %d' % (idx, bar.value[idx])
print

输出,

bar.value[0] = 1
bar.value[1] = 2
bar.value[2] = 3
*** ERROR: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
now, bar.value[0] = 0
now, bar.value[1] = 0
now, bar.value[2] = 0
now, bar.value[3] = 0

回调函数

ctypes 通过 CFUNCTYPE 支持回调函数,

例如,

from ctypes import *

libc = cdll.LoadLibrary('libc.so.6')
   
IntArray5 = c_int * 5
ia = IntArray5(5, 1, 7, 33, 99)
   
# CFUNCTYPE(restype, *argtypes, **kw)
CmpFuncType = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
   
def py_cmp_func(a, b):
   if a[0] > b[0]:
       return 1
   elif a[0] < b[0]:
       return -1
   else:
       return 0
       
cmpfunc = CmpFuncType(py_cmp_func)
   
print 'before sort, the ia list is: ',
for item in ia:
   print item,
       
# void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
libc.qsort(ia, len(ia), sizeof(c_int), cmpfunc)
   
print '\nafter sort, the ia list is: ',
for item in ia:
   print item,
print

输出,

before sort, the ia list is:  5 1 7 33 99 
after sort, the ia list is:  1 5 7 33 99

Resize Space

ctypes 提供了 resize 变量占用空间的方法 resize(),注意,只能增大,不能小于原始空间,

例如,

short_array = (c_short * 4)(1, 2, 3, 4)
print 'sizeof(short_array) =', sizeof(short_array)  # get 8, means short_array take 8-byte memory
print 'len(short_array) =', len(short_array)
print 'before resize, short_array is: ',
for idx in range(len(short_array)):
   print short_array[idx],
print
   
try:
   resize(short_array, 4)  # resize short_array to 4-byte, raise error, due to cannot resize smaller than original
except ValueError, e:
   print 'ERROR: %s' % str(e)
       
resize(short_array, 32)
print 'after succeed resize to 32-byte, now sizeof(short_array) =', sizeof(short_array)
print 'after succeed resize to 32-byte, now len(short_array) =', len(short_array)
print 'after reszie, short_array is: ',
for idx in range(len(short_array)):
   print short_array[idx],

输出,

sizeof(short_array) = 8
len(short_array) = 4
before resize, short_array is:  1 2 3 4
ERROR: minimum size is 8
after succeed resize to 32-byte, now sizeof(short_array) = 32
after succeed resize to 32-byte, now len(short_array) = 4
after reszie, short_array is:  1 2 3 4

Other

class cell(Structure):
   pass
cell._fields_ = [('name', c_char_p), ('next', POINTER(cell))]
   
c1 = cell()
c2 = cell()
c1.name = 'foo'
c2.name = 'bar'
c1.next = pointer(c2)
c2.next = pointer(c1) 
   
p = c1
for i in range(10):
   print p.name,
   p = p.next[0]

输出,

foo bar foo bar foo bar foo bar foo bar

ctypes 相对于其它工具,使用起来有点繁琐,而且有很多坑,需要小心谨慎,

例如,

class POINT(Structure):
   _fields_ = [('x', c_int), ('y', c_int)]
       
class RECT(Structure):
   _fields_ = [('a', POINT), ('b', POINT)]
       
p1 = POINT(1, 2)
p2 = POINT(3, 4)
rc = RECT(p1, p2)
   
print 'rc.a.x =', rc.a.x
print 'rc.a.y =', rc.a.y
print 'rc.b.x =', rc.b.x
print 'rc.b.y =', rc.b.y
   
rc.a, rc.b = rc.b, rc.a
   
print 'after swap, bad result due to this is the pointer,'
print 'rc.a.x =', rc.a.x
print 'rc.a.y =', rc.a.y
print 'rc.b.x =', rc.b.x
print 'rc.b.y =', rc.b.y
print 

输出,

rc.a.x = 1
rc.a.y = 2
rc.b.x = 3
rc.b.y = 4
after swap, bad result due to this is the pointer,
rc.a.x = 3
rc.a.y = 4
rc.b.x = 3
rc.b.y = 4
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值