目的
pybind11はc++のコードを簡単にpythonから呼び出せ非常に便利ですが、あまりリファレンスが多くないのと、実行環境によって導入方法が結構異なるので
m1 macでの導入方法をメモ。
pybind11
pybind11は、主に既存のC ++コードのPythonバインディングを作成するために、PythonでC++型を公開する軽量のヘッダー専用ライブラリです。
公式ドキュメント
cythonとかboost pythonとかあるけどpybind11が一番楽でオーバーヘッドも少ない印象。
実行環境
M1 MacBook Air
ProductName: macOS
ProductVersion: 12.3.1
BuildVersion: 21E258
インストール
M1 macはまだまだcondaが使いづらいので今回はpipenvを使用します。
pipenvだとpythonのバージョンがらpipライブラリのバージョン、依存関係まで管理できます。
pipenvの情報はこちら
Macなのでbrewを使います
brew install pipenv
pipenvのパッケージを作業ディレクトリ直下に保管したいので以下のコマンドを実行
export PIPENV_VENV_IN_PROJECT=1
pythonのバージョンを指定します
pipenv --python 3.9.9
pipenvで仮想環境構築
pipenv install
これで作業ディレクトリに.venv
ディレクトリが作成され、中にパッケージが格納されます。
また、Pipfileというテキストファイルも作成され、ここにバージョンの情報が保存されます。
pybind11のインストール
pipenv install pybind11
この時点でPipfileは以下のようになっています
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
pybind11 = "*"
[dev-packages]
[requires]
python_version = "3.9"
pybind11のinclude path
pybind11はheader-only library であるため、headerのincludeパスさえわかっていれば使用できます。
確認方法は
python3 -m pybind11 --includes
です。おそらくhomebrewは以下のパスもしくは.venvは以下のincludeのパスが表示されていると思います。
c++モジュール作成
何はともあれinstallが環境したらcppのコードを書きます。公式ドキュメントから簡単な和算の関数を作成します
ちなみにここでvscodeで作業する場合は.vscode/c_cpp_properties.json
内に先ほどの
python3 -m pybind11 --includes
の結果のパスを入れることでvscodeがpybind11を認識してくれます
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function that adds two numbers");
}
#include でpybind11を呼び出して、add関数を作成した後、
PYBIND11_MODULE(example, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function that adds two numbers");
}
でpythonで呼び出せる形式を整えています。
ここでm.def("add", &add, "A function that adds two numbers");
についてpython側で呼び出す際の引数名やdocsを定義しています。
c++側の準備は以上となります。
コンパイル & pythonから呼び出し
ここまでできたらあとはコンパイルしてpythonからimportできる形式のファおりに変換して、それをpythonからimportするだけです。
公式ドキュメントにMacOS用のコンパイルコマンドが乗っているのでそれをそのまま使用します。exampleのところは作成したcppファイル、作成するbuiltファイル名を好きにつけることができます。
c++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
そうするとexample.cppのあったディレクトリに新しいファイルが作成されています
example.cpython-39-darwin.so
.cpython-39-darwin.so
といいうのはpythonがc/c++のビルドされたファイルを読み込むための拡張子でosによって変わったりします。今回のコマンドではpython3-config --extension-suffix
で取得した拡張子をそのまま使用しているので問題ないです。
ビルドに管んする情報はここに色々記載があります。
作成した関数をpythonから呼び出してみます。
bash-3.2$ python
Python 3.9.9 (main, Nov 21 2021, 03:16:13)
[Clang 13.0.0 (clang-1300.0.29.3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import example
>>> example.add(1, 2)
3
以上!c++のコードをpythonで使用することができました!
その他エラー
clang: error:
最初Linux用のbuildコマンドを使用したのでエラーが出てしまいました
# linux用のコマンド
c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
っていうg++でよくあるエラーが出てくるので要注意です。(このエラー調べてもまずpybind11には辿り着けません。)
miniforge3
M1Macでcondaを使用するかたはminiforge3から使用する方も多いかもしれません。私の環境ではminiforge3からconda createで仮想化環境を構築してpybind11を導入したところ、ビルドはできたんですが、pythonでimportはできませんでした。(import error)
原因はついにわかりませんでしたが、
.cpython-39-darwin.so
を読み込む他のライブラリ(つまりc/c++の力を借りているpythonライブラリ)はM1 Macでよくエラーを見かけるのでまだM1が対応していないのかもしれないですね