1.前言
C/C++语言的优点是执行效率高,但开发效率和内存安全性较低,比较考虑编程水平。在开发算法的过程中,使用python语言能够快速的完成算法原型的设计并验证算法,但在实际应用过程中一般难以有好的速度效率,因而需要使用C。据笔者在项目实践中,一般而言C语言比python语言在算法执行速度上快约100多倍(windows和linux平台)。
2.Ctypes
ctypes是Python的内置库,用于加载动态链接库并创建能够直接传入C函数的对象。
3.调用方法
笔者在搜集了相关资料后,总结出了以下几种常见用法。C语言实际开发过程中比较麻烦,但Ctypes只能调用C类型而不能调用C++类型。没关系,可以在传入传出参数使用C类型,而中间开发过程中可以使用C++、STL库,最后导出方式为extern "C"。
3.1Python端
为了方便起见,可以传入结构体指针,首先定义结构体:
class InputData(Structure):
_fields_=[("a",c_int),#整型变量
("b",c_double),#浮点型变量
("c",POINTER(c_int)),#整型变量指针(一维数组)
("d",POINTER(c_double)),#浮点型变量指针(一维数组)
]
#创建对象
input_data=InputData()
#对象赋值
input_data.a=1
input_data.b=2.0
#数组赋值
cc=[1,1,1]
input_data.c=(c_int*len(cc))(*cc)
dd=[2.0,2.0,2.0]
input_data.d=(c_double*len(dd))(*dd)
同理也可以定义输出结构体:
class OutputData(Structure):
_fields_=[("m",c_int),
("n",c_double),
("p",POINTER(c_int)),
("q",POINTER(c_double)),
]
3.2 C端
首先定义输入输出结构体,特别注意的是,参数类型和名称顺序一定要对应!
//输入结构体
struct InputData {
int a;
double b;
int* c;
double *d;
};
//输出结构体
struct InputData {
int m;
double n;
int* p;
double *q;
};
在C中定义导出的函数:
//windows平台
extern "C" _declspec(dllexport) void fuction(InputData * input_data, OutputData * output_data);
//linux平台:
extern "C" __attribute__((visibility("default"))) void fuction(InputData * input_data, OutputData * output_data);
在定义的函数中实际上可以使用任意的C++类型,但要注意的是,由于C++有很多个版本的标准,在不同标准下的某些代码编写规范不同,需要注意!可能在C++17是允许写的,在C++11上编译就通不过。
3.3编译动态库
编译命令
//windows平台
cl -O2 /LD 你的C代码文件.cpp
//linux平台
g++ 你的c代码文件.cpp -fPIC -shared -o 你的输出文件名.so
由此得到了.dll库或者.so库
3.4 调用动态库
#windows平台
dll=cdll.LoadLibrary("你的动态库名字.dll")
dll.fuction.argtypes=(POINTER(InputData),POINTER(OutputData))#结构体输入数据,结构体输出数据
get_c=dll.fuction(input_data,output_data)
#linux平台
dll=cdll.LoadLibrary("你的动态库名字.so")
dll.fuction.argtypes=(POINTER(InputData),POINTER(OutputData))#结构体输入数据,结构体输出数据
get_c=dll.fuction(input_data,output_data)
访问返回结果的方法也很简单:
print(output_data.m)
print(output_data.n)
#传出数据中,m作为p的数据长度,C语言的数组传出时跟一个长度较好。
for i in range(output_data.m):
print(output_data.p[i])