(with code) getting started | How to use C/C++ to call Python scripts

Editor's Recommendation

When developing C/C++, many non-core functions can be implemented using Python, so it is also necessary to learn how to call Python programs in C/C++ programs. There are many ways to achieve it, and today I will introduce to you a method that I think is the most convenient.

The article will introduce a method (Python/C API) to enrich your C/C++ application program by embedding Python, which will enable your application program to use Python without changing the original function of the program. Programming language other than C/C++ language to implement some functions of the application.

This approach can be used for several purposes, the main purpose is to allow us to customize the application according to our needs by writing some scripts in Python (certain functions can be more easily written in Python). When you embed Python, the main program generally has nothing to do with the Python implementation, and some parts of the application will call the Python interpreter when needed to run some Python code we wrote. So, if you need to embed Python, then you first need a C/C++ main program.

Basic usage

Python provides a set of C API libraries, so that developers can easily call Python modules from C/C++ programs. C++ users should note that although the API is completely defined in C, the header file has declared the entry point as extern "C", so the API does not need to do anything special to use this API in C++.

insert image description here
For specific documents, refer to the official guide: https://docs.python.org/3.9/extending/embedding.html

If we need to use this set of API, we need to introduce a header file and a library file, taking Cmake build as an example, we need to add in CMakeLists.txt:

find_package (Python COMPONENTS Interpreter Development REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE ${Python_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${Python_LIBRARIES})

In the Windows environment, it should be necessary to ensure the existence of the following two environment variables (not yet tested).

insert image description here
Then we can introduce the corresponding header file in main.cpp:

#include <Python.h>

After that, one of the things the main program does is initialize the Python interpreter:

Py_Initialize();

Of course, since the interpreter needs to be initialized, as code that can be written in C language, we generally need to release it (when not needed):

Py_Finalize();

Now we can pass the Python statement we want to run to the Python interpreter as a string:

PyRun_SimpleString("print("Hello world!")");

give a simple chestnut

This method has no way to transfer values ​​(we don’t consider converting to strings for the time being, after all, it is troublesome to receive data at the same time, which is also a big problem):

1. Initialize the Python interpreter;
2. Pass the code in the form of a string;
3. Release the python interpreter.

code show as below:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int main(int, char **)
{
    
    
    Py_Initialize();                                       // 初始化python解释器
    PyRun_SimpleString("import matplotlib.pyplot as plt"); // 运行python代码
    PyRun_SimpleString("plt.plot([1,2,3,4], [12,3,23,231])");
    PyRun_SimpleString("plt.show()");
    Py_Finalize(); // 释放python解释器
};

The result is as follows:

insert image description here
Obviously, we successfully call Python in the C++ program and draw the image and display it.

Common Data Types

There is a saying in python called "everything is an object", which can be better understood in combination with the source code.
In python, when all variables, functions, classes, etc. are executed in the interpreter, a new object will be created in the heap and the name will be bound to the object.

In C/C++, all Python types are declared as PyObject. In order to be able to operate Python data, Python provides conversion operations between various data types and C language data types:

PyObject *object;  // 创建python的object的指针
Py_DECREF(object); // 销毁object

1. Number and string processing
The Py_BuildValue() function is provided in the Python/C API to convert numbers and strings into corresponding data types in Python.

PyObject* Arg = Py_BuildValue("(i, i)", 1, 2);  //i表示创建int型变量

2. List operation
The PyList_New() function is provided in the Python/C API to create a new Python list. The return value of the PyList_New() function is the created list.

3. Tuple operation
The PyTuple_New() function is provided in the Python/C API to create a new Python tuple. The PyTuple_New() function returns the created tuple.

4. Dictionary operation
The PyDict_New() function is provided in the Python/C API to create a new dictionary. The PyDict_New() function returns the created dictionary.

5. cv::Mat operation
cv::Mat should be the format we will often use, the reference code is as follows:

PyObject *cvmat2py(cv::Mat &image)
{
    
    
    import_array();
    int row, col;
    col = image.cols; //列宽
    row = image.rows; //行高
    int channel = image.channels();
    int irow = row, icol = col * channel;
    npy_intp Dims[3] = {
    
    row, col, channel}; //图像维度信息
    PyObject *pyArray = PyArray_SimpleNewFromData(channel, Dims, NPY_UBYTE, image.data);
    PyObject *ArgArray = PyTuple_New(1);
    PyTuple_SetItem(ArgArray, 0, pyArray);
    return ArgArray;
}

For more operations, please refer to: https://docs.python.org/zh-cn/3/c-api/arg.html#building-values

Take a slightly more complicated chestnut

This method can transfer values:

1. Initialize the Python interpreter;
2. Convert data from C++ to Python;
3. Use the converted data as a parameter to call the Python function;
4. Convert the function return value to a C++ data structure;
5. Release the python interpreter.

The C++ code is as follows:


#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <iostream>

int main(int, char **)
{
    
    
    // 初始化python解释器
    Py_Initialize();
    if (!Py_IsInitialized())
    {
    
    
        return -1;
    }

    // 添加编译后文件的所在路径
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");

    // 要调用的python文件名
    auto pModule = PyImport_ImportModule("common"); // 记得复制到编译后文件的路径下,同时不用加后缀名“.py“
    if (!pModule)
    {
    
    
        std::cout << "Can't find your xx.py file.";
        getchar();
        return -1;
    }

    //获取模块中的函数
    auto pFunc = PyObject_GetAttrString(pModule, "add"); //从字符串创建python函数对象

    // 参数类型转换
    PyObject *pArg = Py_BuildValue("ii", 1, 2);

    //调用直接获得的函数,并传递参数
    auto *py_result = PyEval_CallObject(pFunc, pArg);

    int c_result;
    // 将python类型的返回值转换为C类型
    PyArg_Parse(py_result, "i", &c_result);
    std::cout << "return: " << c_result << std::endl;
};

The Python code is as follows (named common.py):

def add(a, b):
    print('a: ', a)
    print('b: ', b)
    return a + b

The result is as follows (the python file search path set in the code is the compiled file path):

insert image description here
In this way, the two relatively simple implementations are completed. For more operations, please refer to: https://docs.python.org/3.9/c-api/index.html#c-api-index

References

1.https://blog.csdn.net/pipisorry/article/details/49532341
2.https://blog.csdn.net/qq_45401419/article/details/123562089
3.https://zhuanlan.zhihu.com/p/146874652

Guess you like

Origin blog.csdn.net/weixin_41006390/article/details/126191970