个人测试下来Debug生成的dll改pyd,py中import会报错gilstate->autoInterpreterState
如果遇到同样问题使用Release吧
目录
1.安装pybind11
1.pip:
2.github:
2.配置VS工程
2.在VC目录中的包含目录添加:
3.在VC目录中的库目录添加:
4.在链接器-输入-添加依赖项中添加:
3.生成dll
4.快捷更新
1.创建新的启动项目
2.添加引用
3.编写dll2pyd
1.安装pybind11
pybind11是一个简化python调用c++的库,其使用了许多c++11特性来简化流程
安装pybind有许多方式,以下列举两种:
1.pip:
使用pip install pybind11,便可以直接安装pybind11。
对应安装目录在Python目录的Lib文件夹下的site-packages文件夹下的pybind11文件夹。
2.github:
直接去github官方页面下载,解压到你想安装的位置
官方链接:https://github.com/pybind/pybind11
2.配置VS工程
vs版本需要在2017及以上
1.将配置类型(输出)改成dll
2.在VC目录中的包含目录添加:
1.pybind安装目录下的include文件夹
2.Python文件夹中的include文件夹
3.在VC目录中的库目录添加:
Python文件夹中的libs文件夹
4.在链接器-输入-添加依赖项中添加:
对应的lib文件
比如我是Python39,在Python文件夹的libs文件夹中有:
1.python3.lib
2.python3_d.lib
3.python39.lib
4.python39_d.lib
所以在配置为Debug时添加带有后缀_d的lib(上面的2与4)
Release中添加不带_d的(1与3)
但其中python3与python3_d.lib好像也可以不用添加
3.生成dll
可以使用官方的代码先做个测试
想学更多的可以看官方文档(英文):First steps - pybind11 documentation
#include <pybind11/pybind11.h>int add(int i, int j) {return i + j;
}PYBIND11_MODULE(example, m) {m.doc() = "pybind11 example plugin"; // optional module docstringm.def("add", &add, "A function that adds two numbers");
}
注意其中PYBIND11_MODULE中第一个参数为你取module的名字
需要与最终生成的dll文件名字(vs默认是工程名字)(你也可以生成了文件,后改文件名)
py中import的名字一致
最终将你生成的dll后缀名改为.pyd放入你python工程中或者和你运行py的文件目录同级,然后import进行使用
$ python
Python 3.9.10 (main, Jan 15 2022, 11:48:04)
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.add(1, 2)
3
>>>
4.快捷更新
如果每次生成dll,我们都要到对应的目录找到dll后改名再放入py目录中就太麻烦了
这时我们可以使用vs多项目间的引用功能
1.创建新的启动项目
在同解决方案内创建一个新的控制台项目
红框内是上述配置好用于生成dll的项目(MyCPPLib)
而dll2pyd这个项目是用于新创建的自动更新的控制台项目
创建好后,将项目的启动项目设置为dll2pyd
2.添加引用
在dll2pyd中的引用里添加MyCPPLib
那么添加后,只要点击调试,vs就会运行我们dll2pyd的代码
而dll2pyd引用了MyCPPLib,故如MyCPPLib需要更新,则会在运行dll2pyd前自动更新
3.编写dll2pyd
那么显然此时我们只需要在dll2pyd的main函数实现
能够让生成dll自动覆盖目标位置pyd的代码即可
此时我们便可以更改MyCPPLib文件的源码
然后直接点击调试运行便可以直接在Pycharm(或者其他py)中看到效果
以下是我的一个简单实现:
读取MyCPPLib(解决方案名字)/dll2pyd(项目名字)/config.ini配置文件
#include <fstream>
#include <iostream>
using namespace std;
string source;
string target;void read_ini()
{ifstream fs("config.ini", ios_base::in);string temp;fs >> temp;fs >> source;fs >> temp;fs >> target;fs.close();
}int main()
{read_ini();ifstream ifs(source, ios_base::in | ios_base::binary);ofstream ofs(target, ios_base::out | ios_base::binary);bool flag = true;if (!ifs.is_open())cout << "open source[" << source << "] failed" << endl, flag = false;if (!ofs.is_open())cout << "open target[" << target << "] failed" << endl, flag = false;if (flag)ofs << ifs.rdbuf();ifs.close();ofs.close();return 0;
}