编者荐语
前一篇文章我们介绍了如何使用C/C++调用Python脚本,本篇文章我们补齐另一个开发中很常见的问题。在我们使用 Python 进行开发的时候,由于一些算法是计算密集型的,就会导致过多的运行时间,这时我们就可以将这些算法使用 C/C++ 语言来实现,从而减少程序的运行时间。
使用Python很方便,但是在效率上比不上 C/C++ ;再者,某些时候我们想把一些关键性代码保护起来,我们便可以将这些代码编译成 C/C++ 的动态库,然后使用 Python 去调用,能够很好的实现这个目的。
一般有两种常用的实现方法:
使用 Python 扩展编写 C/C++ 代码,然后使用 import 调用动态库
使用 Python 的 ctypes 模块直接加载 C/C++ 动态库(使用常见的 C/C++ 代码编写方式)。
虽然都是编译为动态库,但较大差距在 C/C++ 代码的编写上。
不同实现方法的区别
第一种方法是 Python 为整合其它语言而存在的一种扩展机制,不限于 C/C++ 语言,小编个人感觉第二种方式比较便于理解与实现(通常情况下不需要对已有 C/C++ 代码进行改写,或只需进行少量改写),所以今天就用这种方式给大家介绍一下具体的实现过程。
针对第一种方式的实现,大家可以参考:https://docs.python.org/zh-cn/3/c-api/intro.html
到了这里,可能会有很多同学说 Cython ,是的, Cython 是会解决 Python / ctypes 的一系列问题(例如速度瓶颈),但是过多的学习成本可能不是你开始做一些简单事情时就可以接受的,而 ctypes 更擅长做简单的事情并快速运行,尤其是对 有 C/C++ 和 Python 经验的程序员来说。针对 Cython 与 ctypes 的选择,大家可以参考一下这里:https://www.codenong.com/1942298/
简单介绍一下库
库的本质就是一个打包好的代码包,通常可以分为静态库(.lib .a)和动态库(.dll .so),前者一般为 windows 环境下的后缀,后者为 linux 环境下的后缀。静态库在主程序编译时,会被一并编译到最终的可执行文件中,而动态库则是会在调用时再去查找相关的内容。因为 ctypes 并没有办法调用静态库,所以我们本次会将 C/C++ 编译为动态库。
基本使用方法
个人感觉使用 ctypes 的优点就是方便,极小的修改换来极大的性能提升,是一种效率与性能平衡下的最优选择。调用过程一般就分为两步:
1.将 C/C++ 代码编译为动态库;
2.使用 Python 调用相应的动态库。
将 C/C++ 代码编译为动态库
CMake记得修改一下:
add_library(c_library SHARED src/c_library.cpp)
此处的 SHARED 表示的就是动态链接库。
ctypes不会知道你正在使用的库中的#define常量和东西,只知道函数,因此你必须在自己的代码中重新定义这些常量。
一个简单的求和代码如下所示(命名为:c_library.cpp):
#include <stdio.h>
extern "C"
{
int add_int(int num1, int num2)
{
return num1 + num2;
}
float add_float(float num1, float num2)
{
return num1 + num2;
}
}
特别注意在调用C++函数需要在函数声明时,加入前缀 extern “C” ,这是由于C++支持函数重载功能,在编译时会更改函数名。所以在函数声明时,我们默认加入前缀 extern “C” 确保代码按 C 的方式进行编译。
编译一下,生成的动态库如下图所示:
使用 Python 调用动态库
Python 调用动态链接库的流程一般分为步:
1.加载动态链接库;
2.将输入转换为 C 兼容的数据类型;
3.将输出转换为 C 兼容的数据类型;
4.调用动态库中的函数即可。
完整的代码如下所示:
import ctypes
import os
import sys
#Load the shared object file
if sys.platform == "win32":
c_library = ctypes.CDLL(os.path.abspath(
'.')+"/out/build/x64-Debug/c_cxx/c_library/libc_library.dll")
print
elif sys.platform == "linux":
c_library = ctypes.CDLL(os.path.abspath(
'.')+"/build/c_cxx/c_library/libc_library.so")
#Find sum of integers
res_int = c_library.add_int(4, 5)
print("Sum is : " + str(res_int))
#Find sum of floats
a = ctypes.c_float(5.5) # 输入转换为C兼容的数据类型
b = ctypes.c_float(4.1)
c_library.add_float.restype = ctypes.c_float # 输出转换为C兼容的数据类型
print("Sum is : ", str(c_library.add_float(a, b)))
int 型我没记错的话是不用转换的,所以我就没写代码。
运行一下 Python 代码,结果如下所示:
更多 ctypes 相关操作可以参考:https://docs.python.org/3/library/ctypes.html
ctypes 中常用变量类型与 C 类型对应表:
参考资料
1.https://zhuanlan.zhihu.com/p/36772947
2.https://blog.csdn.net/u012247418/article/details/80170690