Cpp和Python相互调用
记录一下在Cpp程序和Python脚本中相互调用的方法。
Cpp调用Python
在C++程序中调用Python解释器,包括执行简单Python语句(字符串形式),以及执行整个py脚本(文件形式)。
执行简单命令
最简单的例子:调用Python解释器,输出HelloWorld,C++源文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
Py_Initialize();
PyRun_SimpleString("print('Hello, world! (from Python)')");
Py_Finalize();
return 0;
}
注意在导入Python.h
时进行了一些处理,因为我们的电脑中通常只有Release版本的Python库,并没有Debug版本。
我们使用CMake完成Python库的导入,主要语句如下 1
2
3find_package(Python REQUIRED COMPONENTS Interpreter Development)
add_executable(test main.cpp)
target_link_libraries(test PRIVATE Python::Python)
正常编译执行即可。
执行Python脚本
假设我们在path
路径下提供了demo.py
脚本,例如
1
2
3
4
5
6
7
8
9
10
11
12import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]
plt.plot(x, y)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Sample Plot')
plt.grid(True)
plt.savefig('plot.png')
将C++源文件改为下面的内容 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
int main() {
Py_Initialize();
FILE *file = fopen("path/demo.py", "r");
if (file != nullptr) {
PyRun_SimpleFile(file, "demo.py");
fclose(file);
}
else {
fprintf(stderr, "Failed to open Python script file\n");
return 1;
}
Py_Finalize();
return 0;
}
此时我们就可以让C++程序在执行时自动调用Python解释器执行demo.py
脚本。
Python调用Cpp
我们将Cpp程序封装为动态库(保证提供C语言接口),在Python中使用内置的ctypes
模块所提供的工具可以进行调用。
例如下面的动态库myfunc.dll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extern "C" {
__declspec(dllexport) int add(int a, int b) { return a + b; }
__declspec(dllexport) void hello_world(char *buffer, int buffer_size) {
const char *message = "Hello, World!";
int message_length = static_cast<int>(strlen(message));
if (message_length < buffer_size) { strcpy(buffer, message); }
else {
strncpy(buffer, message, buffer_size - 1);
buffer[buffer_size - 1] = '\0';
}
}
}
提供两个函数:
add
函数:返回两个整数的和;hello_world
函数:在传入的buffer中填入helloworld字符串。如果直接在C++中输出到控制台,在Python调用时是看不见的;也不能直接返回一个const char*
字符串指针或者C++的std::string
,都比较麻烦,传入字符串缓冲区是最简便的实现。
在Python中可以使用下面的方式调用这两个接口 1
2
3
4
5
6
7
8
9
10
11
12import ctypes
mylib = ctypes.CDLL("./bin/myfunc_d.dll") # myfunc.dll or myfunc_d.dll
result = mylib.add(5, 3)
print(f"Result of adding: {result}, type is {type(result)}")
buffer_size = 20
buffer = ctypes.create_string_buffer(buffer_size)
mylib.hello_world(buffer, buffer_size)
hello_world_string = buffer.value.decode("utf-8")
print(hello_world_string)
注意:对于Jupyter Notebook,在加载了对应的动态库之后不会自动卸载,系统会保护正在使用的动态库,如果此时尝试修改动态库则会编译报错。