シナリオによっては、C++ よりもスクリプト言語を呼び出して一部の関数を実装する方が便利な場合があります。
ここで紹介したいのは pybind11 です。pybind11 は "前身" Boost.Python を借用しており、C++ と Python を自由に切り替えることができ、C++ ベクトルを Python リストに変換するなど、2 つの言語要素を任意に変換できます。 group は C++ タプルに変換され、C++ で Python スクリプトを呼び出すだけでなく、Python で C++ 関数やクラスを呼び出すこともできます。
pybind11 の名前の「11」は、最新の C++ 開発 (C++11 以降) に完全に基づいているため、古いシステムとの互換性の負担がないことを意味します。最新の C++ 機能を多数使用しており、コードがクリーンで整然としているだけでなく、より効率的に実行されます。
pybind11 は主に C++ で Python を拡張することに重点を置いていますが、その逆、つまり C++ プログラムに Python インタープリターを埋め込むこともできます。
Python での C++ の呼び出しに関する記事は数多くありますが、この記事では主に C++ で Python を呼び出す方法に焦点を当てます。
前提
Python と対話するため、Python をインストールする必要があります。Python は通常 Linux システムに含まれています。
C++ から Python を呼び出す
インタープリターが埋め込まれた基本的な実行可能ファイルは、CMake と のわずか数行pybind11: :embed
で作成できます。
公式の CMake の例:
cmake_minimum_required(VERSION 3.4)
project(example)
find_package(pybind11 REQUIRED) # or `add_subdirectory(pybind11)`
add_executable(example main.cpp)
target_link_libraries(example PRIVATE pybind11::embed)
ここでは、利便性を高めるために CMake の FetchContent を使用します。
cmake_minimum_required(VERSION 3.20)
project(CppBindPythonDemo)
set(CMAKE_CXX_STANDARD 17)
include(FetchContent)
FetchContent_Declare(
pybind11
GIT_REPOSITORY https://github.com/pybind/pybind11.git
)
FetchContent_MakeAvailable(pybind11)
add_executable(CppBindPythonDemo main.cpp)
target_link_libraries(CppBindPythonDemo PRIVATE pybind11::embed)
Pythonコードを実行する
Python コードを実行するには、いくつかの異なる方法があります。1 つのオプションはeval
、 、exec
またはeval _ file
こんにちは世界:
//main.cpp
#include <pybind11/embed.h> // everything needed for embedding
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{
}; // start the interpreter and keep it alive
py::print("Hello, World!"); // use the Python API
}
例 1:
#include <pybind11/embed.h>
namespace py = pybind11;
int main() {
py::scoped_interpreter guard{};
py::exec(R"(
kwargs = dict(name="World", number=42)
message = "Hello, {name}! The answer is {number}".format(**kwargs)
print(message)
)");
}
例 2:
#include <pybind11/embed.h>
namespace py = pybind11;
using namespace py::literals;
int main() {
py::scoped_interpreter guard{};
auto kwargs = py::dict("name"_a="World", "number"_a=42);
auto message = "Hello, {name}! The answer is {number}"_s.format(**kwargs);
py::print(message);
}
例 3:
#include <pybind11/embed.h>
#include <iostream>
namespace py = pybind11;
using namespace py::literals;
int main() {
py::scoped_interpreter guard{};
auto locals = py::dict("name"_a="World", "number"_a=42);
py::exec(R"(
message = "Hello, {name}! The answer is {number}".format(**locals())
)", py::globals(), locals);
auto message = locals["message"].cast<std::string>();
std::cout << message;
}
ご覧のとおり、共通点が 1 つあります。Python コードを実行する前に、最初にコードが作成されます。py::scoped_interpreter guard{};
Python API (pybind11 のすべての関数とクラスを含む) を使用する前に、インタープリターを初期化する必要があります。scoped_interpreter
RAII クラスは、インタープリターのライフサイクルを担当します。scoped_interpreter
有効期間が終了すると、インタープリタはシャットダウンしてメモリをクリーンアップし、その後は Python 関数を呼び出すことができなくなります。
インポートモジュール
上記の実行方法に加えて、Python モジュールをインポートしたり、システム モジュールや自己作成したりすることもできます。
システム モジュールを呼び出します。
py::module_ sys = py::module_::import("sys");
py::print(sys.attr("path"));
py::module math = py::module::import("math");
py::object result = math.attr("sqrt")(25);
std::cout << "Sqrt of 25 is: " << result.cast<float>() << std::endl;
自分で書いたモジュール(pyファイル)を呼び出します。
py_bind_demo.py
ここで関数fib
とクラスを含むファイルを書きましたPyClass
#py_bind_demo.py
def fib(n):
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a + b
print()
class PyClass:
__name = "py_module"
def print_name(self):
print("call python print_name func")
print(self.__name)
def fib(self, n):
"""Print a Fibonacci series up to n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a + b
print()
main.cpp
次に、
//用py::object导入python类模块并进行类的初始化
py::object PyClass = py::module::import("py_bind_demo").attr("PyClass");
//创建类对象
py::object pyClass = PyClass();
//调用类成员函数
pyClass.attr("print_name")();
pyClass.attr("fib")(4);
コンパイルして実行すると、結果はエラーになります
terminate called after throwing an instance of 'pybind11::error_already_set'
what(): ModuleNotFoundError: No module named 'py_bind_demo'
プロンプトでこのモジュールが見つかりません。なぜですか?
分析の結果、ディレクトリ構造の問題により、py ファイルが実行可能ファイルのパスにあるのではなく、 minemain.cpp
とpy が同じパスにあることが判明したため、実行可能ファイルを実行するときに、モジュールpy_bind_demo.py
見つからない。
したがって、Python を呼び出すときは、実行可能ファイルのパスに py ファイルを置く必要があります。
図に示すように:
前 | 後 |
---|---|
これで正常に実行できるようになりました
要約する
この記事では、C++ で Python を呼び出す方法を簡単に紹介しますが、まだ詳細が説明されていない部分も多くあるため、関連するドキュメントをご自身で確認してください。