由于有用C++调用Python程序的需求,本文记录一种可行的方式,采用cpython打包库,在C++中加载解释器调用程序。

准备工作

安装CPython

1
pip install cython

示例过程

  1. 建立一个.pyx文件用于暴露接口。如example.pyx
1
2
3
4
5
6
7
8
# example.pyx

cdef extern from "math.h":
double sqrt(double x)

def c_sqrt(double x):
return sqrt(x)

  1. 创建一个setup.py文件,用于构建Cython代码成为一个可被C++调用的扩展模块
1
2
3
4
5
6
7
from setuptools import setup
from Cython.Build import cythonize

setup(
ext_modules=cythonize("example.pyx"),
)

  1. 构建生成拓展模块
1
python setup.py build_ext --inplace

此时会产生如下文件结构,example.cp39-win_amd64.pyd这将是被我们调用的库。

  1. 建立main.cpp, 把main函数第一行的位置路径改为你的文件目录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <Python.h>
#include <iostream>
#include <cstdlib> // for _putenv

int main()
{
// 设置 PYTHONHOME 环境变量
_putenv("PYTHONHOME=E:\\conda\\envs\\cpy");

Py_Initialize();

// 添加当前目录到 Python 模块搜索路径
PyObject *sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString("."));

// 打印 sys.path 内容
PyObject* repr = PyObject_Repr(sysPath);
PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
const char *bytes = PyBytes_AS_STRING(str);
std::cout << "sys.path: " << bytes << std::endl;
Py_XDECREF(repr);
Py_XDECREF(str);

PyObject *pName, *pModule, *pFunc;
PyObject *pArgs, *pValue;

// 导入模块
pName = PyUnicode_DecodeFSDefault("example");
pModule = PyImport_Import(pName);
Py_DECREF(pName);

if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, "c_sqrt");
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_Pack(1, PyFloat_FromDouble(16.0));
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL) {
std::cout << "Result of call: " << PyFloat_AsDouble(pValue) << std::endl;
Py_DECREF(pValue);
} else {
PyErr_Print();
std::cerr << "Call failed" << std::endl;
}
} else {
if (PyErr_Occurred())
PyErr_Print();
std::cerr << "Cannot find function 'c_sqrt'" << std::endl;
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
} else {
PyErr_Print();
std::cerr << "Failed to load 'example'" << std::endl;
}

Py_Finalize();
return 0;
}
  1. 编译程序,这里要特别注意使用同一个python解释器环境,如果你使用虚拟环境,则应该把命令中的路径更改为你虚拟环境中的路径,-lpython39改为自己libs里面的动态库名称。
1
g++ -o main main.cpp -IE:\conda\envs\cpy\include -LE:\conda\envs\cpy\libs -lpython39

这时运行程序可以看到已经成功运行了。

该封面图片由KristenPixabay上发布