Make Cpp Work with Python
C++
Python是世界上最难学好的语言!
本文中,你将看到如何配置一个同时使用Python和C++两种语言的项目。
前言
Python 高度封装又非常易用,同时在很多情况下也是进行机器学习等任务的唯一选择。另一方面,如果调教得当,C++则在数据集的读取及预处理上具备性能上的显著优势。将二者作为“联合主演”,融入到一个project中是一个值得考虑的选项。
开发工具
Python 没什么好说的。对于C++,如果是在Linux平台下,常用的工具是g++
& make,输出的动态库后缀是.so。在Windows下,我使用了Visual Studio -
不得不说相比
apt install make gcc
,VS的安装配置痛苦许多,但VS也确实没什么好的替代(不知道为什么,我就是不想用MinGW或者CMake这种跨平台的工具)。
因此,这里我们使用VS编译出的供Python调用的C++库也是面向Windows的版本。我安装的使用C++的桌面开发组件的最小集合是(VS 2022):
- MSVC:C++编译器及库
- Windows 10 SDK:包含了一些C++ API在Windows平台的定义与实现(如cstring)。如果未安装,在调用一些NT与POSIX的实现不一样的C++ API时无法顺利编译,提示找不到头文件。
- 实时调试器:就是字面意思
流程及注意事项
- 在VS中,新建C++项目,注意以下几条项目属性(项目-属性)
- 常规-配置类型:动态库(.dll)
- 常规-C++语言标准
- 高级-目标文件拓展名:.dll
- 高级-使用调试库:发布时,选择否
- 完成C++项目
- 在开放给Python调用的函数前,添加如下装饰符,否则Python无法调用
1
2extern "C" __declspec(dllexport)
void foo() {...} - 无需编写makefile:VS会帮你打理一切!
- 在开放给Python调用的函数前,添加如下装饰符,否则Python无法调用
- 生成项目
- 发布时,选择Release
- 注意区分x86/x64。特别是当你使用了外部的库时
- 在Python中,通过如下方式引入并调用C++-based content:
1
2
3import ctypes
cpp = ctypes.cdll.LoadLibrary(path_to_dll)
cpp.foo()
完成!
...真的会这么顺利吗?
你可能遇到以下问题:
OSError: [WinError 126] 找不到指定的模块。
什么,找不到?不慌,反手一个
assert os.path.exists(path_to_dll)
。什么?文件路径没错?那是怎么会事呢?
这时候就要祭出一个VS自带的小工具 dumpbin
(需要从VS命令行工具中调用)了。它可以对你要引入的dll进行分析,报告其依赖的其他dll。如黄色框所示,待引入的dll依赖于两个dll。至于其依赖的dll需要到哪里去找?如果你编译时引用了第三方的库(如lib静态库),那么第三方的库可能也提供了dll版本。而像kernel32.dll这类dll是Windows系统提供的,它们一般位于Windows的system32或SysWOW64目录下,将这些依赖复制一份到要引入的dll所在目录即可。
最后的最后,如何让Python识别到依赖的dll库文件呢?热心网友提供的一种建议是,使用os.chdir
切换Python的工作目录到dll目录。然而,这种方式不太优雅,而且可能会导致无法import同一项目下自己写的其他模块。我认为的一种比较好的方式是临时修改系统环境变量:
1
2
3
4
5
6# 在windows系统中,os.pathsep即为;
os.environ["PATH"] += os.pathsep + dll_path
dll_file = os.path.join(dll_path, "dll_to_import.dll")
assert (os.path.exists(dll_file))
# 传入的是文件名而不是完整路径,因为路径已经在PATH中
cpp = ctypes.cdll.LoadLibrary(dll_file)
现在应该没有问题了。至于C++怎么写,性能怎么优化……祝你好运!
其他参考
这里有一份MS的文档,但是说实在的,真的有人用VS写Python吗?还是那句话,特定于某种工具(如这里的VS)或平台、又有替代的方法,可能并不值得花功夫去学。尤其是MS搞的这些东西