Toccata in Nowhere.

Python 调用 C / C++ 函数

2020.08.05

本文介绍使用 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.ctypeslibnpct 并使用 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 时候要注意保持位宽的一致性,特别是在传值过程中涉及矩阵数据类型转换的过程,如果位宽不一致会导致传值错误。