個人メモ
Bazel+pybind11でC++のコードからPythonモジュールを作る方法について記述する。
以下では、Ubuntu 22.04のDockerコンテナを使用している。
Bazelのインストール
公式の推奨に従って、Bazeliskを使用する。
npmのインストール
Ubuntuのaptからインストールした後、バージョンアップする。
apt install nodejs npm npm install -g n n stable apt purge nodejs npm
Bazeliskのインストール
npm install -g @bazel/bazelisk
Python3とNumpyのインストール
apt install python3-numpy -y
WORKSPACEファイル作成
作業ディレクトリにWORKSPACEファイルを作成する。
内容は、pybind11_bazelのREADMEに従う。
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "pybind11_bazel", strip_prefix = "pybind11_bazel-master", urls = ["https://github.com/pybind/pybind11_bazel/archive/master.zip"], ) # We still require the pybind library. http_archive( name = "pybind11", build_file = "@pybind11_bazel//:pybind11.BUILD", strip_prefix = "pybind11-2.10.4", urls = ["https://github.com/pybind/pybind11/archive/v2.10.4.tar.gz"], ) load("@pybind11_bazel//:python_configure.bzl", "python_configure") python_configure(name = "local_config_python", python_version = "3")
BUILDファイル作成
srcディレクトリに、BUILDファイルを作成する。
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension") pybind_extension( name = "example", srcs = ["example.cpp"] )
C++のソース作成
srcディレクトリに、C++のソース(example.cpp)を作成する。
以下の例では、
- 2つの正数を足すadd関数
- リストをプリントするprint_vector関数
- Numpyのndarrayをプリントするprint_ndarray関数
- Numpyのndarrayを返却するget_ndarray関数
の4つの関数を定義している。
#include <pybind11/pybind11.h> #include <pybind11/stl.h> #include <pybind11/numpy.h> #include <iostream> namespace py = pybind11; int add(int i, int j) { return i + j; } void print_vector(const std::vector<int> &v) { for (auto item : v) std::cout << item << " "; } void print_ndarray(py::array_t<int16_t> array) { std::cout << array.size() << std::endl; for (auto item : array) std::cout << item << " "; } py::array_t<int8_t> get_ndarray() { constexpr size_t size = 3; auto result = py::array_t<int8_t>(size); py::buffer_info buf = result.request(); int8_t *ptr = static_cast<int8_t*>(buf.ptr); for (size_t i = 0; i < size; ++i) ptr[i] = (int8_t)i; return result; } PYBIND11_MODULE(example, m) { m.def("add", &add, "A function that adds two numbers"); m.def("print_vector", &print_vector); m.def("print_ndarray", &print_ndarray); m.def("get_ndarray", &get_ndarray); }
ビルド
bazeliskコマンドでビルドする。
ビルドターゲットは、BUILDファイルのpybind_extensionで定義したnameに拡張子.soを付けたものになる。
この例では、example.soとなる。
bazelisk build -c opt src:example.so
ビルドに成功すると、「bazel-bin/src/example.so」にPythonモジュールが出力される。
テスト
bazel-bin/srcに移動して、pythonインタープリタから、テストを行う。
cd bazel-bin/src python3
>>> import example >>> import numpy as np >>> >>> example.add(1, 2) 3 >>> example.print_vector([1, 2, 3]) 1 2 3 >>> >>> example.print_ndarray(np.array([4, 5, 6], dtype=np.int16)) 3 4 5 6 >>> >>> example.get_ndarray() array([0, 1, 2], dtype=int8)