本文介绍使用 Python 调用 C / C++ 编译得到的库文件中的函数。
包依赖
import ctypes
调用方法
将 C / C++ 函数编译为动态库文件
For C
gcc functionsName.c -fPIC -shared -o libName.so
For C++
g++ functionsName.cpp -fPIC -shared -o libName.so
注意:在编译动态库的时候 C++ 函数必须被指定为 extern “C”, 可以使用宏定义关键词,添加到函数声明前:
#define CEXT extern "C"
// then add the macro keywords before function name:
CEXT void helloWorld(){
...
}
导入Python
import ctypes
functionBranch = ctypes.cdll.LoadLibrary("./libName.so")
声明返回值与参数的数据类型
functionBranch.funcName.restype = ctypes.c_float
可选的数据类型有:
| ctypes type | C type | Python Type |
|---|---|---|
| c_char | char | 1-character string |
| c_wchar | wchar_t | 1-character unicode string |
| c_byte | char | int/long |
| c_ubyte | unsigned char | int/long |
| c_bool | bool | bool |
| c_short | short | int/long |
| c_ushort | unsigned short | int/long |
| c_int | int | int/long |
| c_uint | unsigned int | int/long |
| c_long | long | int/long |
| c_ulong | unsigned long | int/long |
| c_longlong | __int64 or longlong | int/long |
| c_ulonglong | unsigned __int64 or unsigned long long | int/long |
| c_float | float | float |
| c_double | double | float |
| c_longdouble | long double float | float |
| c_char_p | char * | string or None |
| c_wchar_p | wchar_t * | unicode or None |
| c_void_p | void * | int/long or None |
对于指针类型: 添加 _p 到以上的数据类型前, 例如 c_int_p 指代 int*。
声明 C-type arrays / Matrix 作为参数
简单的方式可以通过引入 numpy.ctypeslib 为 npct 并使用 ndpointer 函数声明指针:
import ctypes
import numpy as np
import numpy.ctypeslib as npct
radiats = ctypes.cdll.LoadLibrary("./libradiats.so")
radiats.dc_radiat.argtypes = [ctypes.c_float, ctypes.c_float, ctypes.c_float, npct.ndpointer(dtype = ctypes.c_float, ndim = 2, flags="C_CONTIGUOUS")]
aa = np.zeros([3, 3], dtype = ctypes.c_float)
strike = 20
dip = 30
rake = 0
radiats.dc_radiat(strike,dip,rake,aa)
print(aa)
以上代码同样声明了参数的数据类型, ndpointer 声明了一个 ctypes.c_float 的二维指针。
特别注意
C / C++ 的 float 类型位宽为 $32$;而Python 中的 float 类型与 np.float 的位宽为 $64$。因此在声明返回值数据类型 restype 与参数数据类型 argtypes 时候要注意保持位宽的一致性,特别是在传值过程中涉及矩阵数据类型转换的过程,如果位宽不一致会导致传值错误。