记录一下各种语言之间简单地进行相互调用的做法。
Cpp 调用 Python
在C++程序中调用Python解释器,包括执行简单Python语句(字符串形式),以及执行整个py脚本(文件形式)。
执行简单命令
最简单的例子:调用Python解释器,输出HelloWorld,C++源文件如下
main.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #ifdef _DEBUG #undef _DEBUG #include <Python.h> #define _DEBUG 1 #else #include <Python.h> #endif
int main() { Py_Initialize();
PyRun_SimpleString("print('Hello, world! (from Python)')");
Py_Finalize(); return 0; }
|
注意在导入Python.h时进行了一些处理,因为我们的电脑中通常只有Release版本的Python库,并没有Debug版本。
我们使用CMake完成Python库的导入,主要语句如下
1 2 3
| find_package(Python REQUIRED COMPONENTS Interpreter Development) add_executable(test main.cpp) target_link_libraries(test PRIVATE Python::Python)
|
正常编译执行即可。
执行 Python 脚本
假设我们在path路径下提供了demo.py脚本,例如
demo.py1 2 3 4 5 6 7 8 9 10 11 12
| import 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++源文件改为下面的内容
main.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #ifdef _DEBUG #undef _DEBUG #include <Python.h> #define _DEBUG 1 #else #include <Python.h> #endif
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
myfunc.cpp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <cstring>
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 12
| import ctypes
mylib = ctypes.CDLL("./bin/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,在加载了对应的动态库之后不会自动卸载,系统会保护正在使用的动态库,如果此时尝试修改动态库则会编译报错。
MATLAB 调用 Cpp
首先,我们简单创建一个动态库,由于我们的实验环境是Windows+MSVC,需要注意动态库的符号导出问题,动态库包括头文件demo.h和源文件demo.cpp
demo.h1 2 3 4 5 6 7 8 9 10 11 12 13
| #pragma once
#ifdef MY_EXPORTS #define MY_API __declspec(dllexport) #else #define MY_API __declspec(dllimport) #endif
extern "C" { MY_API int add(int a, int b);
MY_API double multiply(double a, double b); }
|
demo.cpp1 2 3 4 5 6
| #define MY_EXPORTS #include "demo.h"
int add(int a, int b) { return a + b; }
double multiply(double a, double b) { return a * b; }
|
然后在Release模式下编译得到demo.dll,将其和头文件一起放在同一个目录中,使用下面的MATLAB脚本进行测试
test.m1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| loadlibrary('demo.dll','demo.h');
libisloaded('demo');
libfunctions('demo');
calllib('demo', 'multiply', 5.20, 13.14) calllib('demo', 'add', 5, 13)
unloadlibrary('demo')
libisloaded('demo')
|
运行结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| Functions in library demo:
add multiply
ans = 68.3280 ans = 18 ans = logical 0
|
这里的情况比较简单,在更一般的情况下还存在MATLAB数据类型和C++数据类型的转换问题,暂时不作讨论。