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 _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 __int64long long int
c_ulonglong unsigned __int64unsigned long long int
c_size_t size_t int
c_ssize_t ssize_tPy_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 提供了对位域的支持,

例如,

[Python调用C++](javascript:void(0)

上一篇:云主机新增网卡配置多网卡策略路由


下一篇:pintools的简单使用