編集者のおすすめ
C/C++ を開発する場合、多くの非コア関数は Python を使用して実装できるため、C/C++ プログラムで Python プログラムを呼び出す方法を学ぶことも必要です。これを実現するにはさまざまな方法がありますが、今日は私が最も便利だと思う方法を紹介します。
C/C++言語以外のプログラミング言語 Pythonを組み込むことでC/C++アプリケーションプログラムを強化し、本来のプログラムの機能を変えずにPythonを利用できるようにする方法(Python/C API)を紹介します。アプリケーションの一部の機能を実装します。
このアプローチはいくつかの目的に使用できます。主な目的は、Python でいくつかのスクリプトを作成することで、ニーズに応じてアプリケーションをカスタマイズできるようにすることです (特定の関数は Python でより簡単に作成できます)。Python を埋め込む場合、メイン プログラムは通常、Python 実装とは何の関係もありません。また、アプリケーションの一部は、作成した Python コードを実行するために必要な場合に Python インタープリタを呼び出します。したがって、Python を組み込む必要がある場合は、まず C/C++ メイン プログラムが必要です。
基本的な使い方
Python は C API ライブラリのセットを提供するため、開発者は C/C++ プログラムから Python モジュールを簡単に呼び出すことができます。C++ ユーザーは、API が完全に C で定義されているにもかかわらず、ヘッダー ファイルでエントリ ポイントが extern "C" として宣言されていることに注意してください。したがって、この API を C++ で使用するために API は特別なことを行う必要はありません。
特定のドキュメントについては、公式ガイドを参照してください: https://docs.python.org/3.9/extending/embedding.html
この API セットを使用する必要がある場合は、Cmake ビルドを例としてヘッダー ファイルとライブラリ ファイルを導入する必要があり、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})
Windows 環境では、次の 2 つの環境変数が存在することを確認する必要があります (まだテストされていません)。
次に、対応するヘッダー ファイルを main.cpp に導入できます。
#include <Python.h>
その後、メイン プログラムが行うことの 1 つは、Python インタープリターの初期化です。
Py_Initialize();
もちろん、インタプリタは C 言語で記述できるコードとして初期化する必要があるため、通常は (不要な場合に) 解放する必要があります。
Py_Finalize();
これで、実行したい Python ステートメントを文字列として Python インタープリターに渡すことができます。
PyRun_SimpleString("print("Hello world!")");
シンプルな栗を与える
このメソッドには値を転送する方法がありません(文字列に変換することは当面考えていません。結局のところ、データを同時に受け取るのは面倒であり、これも大きな問題です)。
1. Python インタープリターを初期化する;
2. コードを文字列の形式で渡す;
3. Python インタープリターを解放する。
コードは以下のように表示されます。
#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解释器
};
結果は次のとおりです。
明らかに、C++ プログラムで Python を正常に呼び出し、イメージを描画して表示します。
一般的なデータ型
Python には「すべてはオブジェクトである」という格言がありますが、これはソース コードと組み合わせるとよりよく理解できます。
Python では、すべての変数、関数、クラスなどがインタープリターで実行されると、新しいオブジェクトがヒープに作成され、名前がオブジェクトにバインドされます。
C/C++ では、すべての Python 型は PyObject として宣言されます。Python データを操作できるようにするために、Python はさまざまなデータ型と C 言語のデータ型の間の変換操作を提供します。
PyObject *object; // 创建python的object的指针
Py_DECREF(object); // 销毁object
1. 数値と文字列の処理
Py_BuildValue() 関数は、数値と文字列を Python の対応するデータ型に変換するために Python/C API で提供されています。
PyObject* Arg = Py_BuildValue("(i, i)", 1, 2); //i表示创建int型变量
2. リスト操作
PyList_New() 関数は、新しい Python リストを作成するために Python/C API で提供されています。PyList_New() 関数の戻り値は作成されたリストです。
3. タプル操作
PyTuple_New() 関数は、新しい Python タプルを作成するために Python/C API で提供されています。PyTuple_New() 関数は、作成されたタプルを返します。
4. 辞書操作
新しい辞書を作成するための PyDict_New() 関数が Python/C API で提供されています。PyDict_New() 関数は、作成された辞書を返します。
5. cv::Mat 操作
cv::Mat はよく使用する形式です。参照コードは次のとおりです。
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;
}
詳しい操作については、https://docs.python.org/zh-cn/3/c-api/arg.html#building-values を参照してください。
もう少し複雑な栗を取り上げます
このメソッドは値を転送できます。
1. Python インタープリターを初期化する;
2. データを C++ から Python に変換する;
3. 変換されたデータをパラメーターとして使用して Python 関数を呼び出す;
4. 関数の戻り値を C++ データ構造に変換する;
5. Python インタープリターを解放する。
C++ コードは次のとおりです。
#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;
};
Python コードは次のとおりです (common.py という名前)。
def add(a, b):
print('a: ', a)
print('b: ', b)
return a + b
結果は次のようになります (コード内で設定された Python ファイル検索パスはコンパイルされたファイル パスです)。
このようにして、2 つの比較的単純な実装が完了しました。その他の操作については、https://docs.python.org/3.9/c-api/index.html#c-api-index を参照してください。
参考文献
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