C++をPythonから呼び出す
WindowsでC++で作成したdll(so)をPythonから呼び出す機会があったので、備忘録を兼ねてまとめておきます。
2024/04/21現在、C++をPythonから呼び出す主な方法はboost pythonとpybind11があるようです。
本記事ではWindows + Visual Studio 2019 でpybind11を用いてPythonからの呼び出しを試してみます。
動作コードはgithubに置いているため、良ければ参考にしてください。
初めに
Visual studioでソリューションとプロジェクトの作成
Visual studioでソリューションとビルド用プロジェクトを作成しておきます。
C++の空のプロジェクトで適当な名前で作成します。今回はcpp
にしました。
以下のファイルが作成されるはずなので、ビルドが通ることを確認します。
pybind11
pybind11のインストール
boostと同様にpybind11もinstallします。公式を参考にvcpkgでインストールを行います。
vcpkgを持っていない場合
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install pybind11
既にvcpkgがある場合
vcpkg integrate install
vcpkg install pybind11
C++ライブラリの作成
dllmain.cpp
にC++の関数と、それをPythonにdllとして出力するためのコードを書きます。
コードはpybind11 公式から持ってきています。
#include <pybind11/pybind11.h>
int add(int i, int j) {
return i + j;
}
// ! モジュール名"cpp"はプロジェクト名と一致するようにしてください。
PYBIND11_MODULE(cpp, m) {
m.doc() = "pybind11 example plugin"; // optional module docstring
m.def("add", &add, "A function that adds two numbers");
}
プロジェクト設定の変更
追加のインクルードディレクトリの変更
pybind11
が読み込めるよう、pybindをインストールした場所を登録しておきます。
またビルド時にPython.h
を参照するため、dllを読み込むPythonのincludeディレクトリも追加します。以下のような設定になります。
構成はRelease
のx64
にしていますが、この辺りは使用するPythonに合わせてください。
ターゲットファイルの拡張子の変更
ターゲットファイルの拡張子を.dll
から.pyd
に変更しておきます。
これはPythonからimport cpp
で直接読めるようにするためですが、cdll.LoadLibrary
を用いても読み込めるため、好みの問題にはなります。
ビルドの確認
上記の設定を加え、ビルドが正しくできるか確認します。成功した場合、cppのフォルダ構成は以下のようになります。(Pythonについては後述します。)
.
├── cpp
│ ├── cpp.sln
│ ├── cpp.vcxproj
│ ├── cpp.vcxproj.filters
│ ├── cpp.vcxproj.user
│ ├── dllmain.cpp
│ ├── framework.h
│ ├── pch.cpp
│ ├── pch.h
│ └── x64
│ └── Release
│ ├── cpp.Build.CppClean.log
│ ├── cpp.dll.recipe
│ ├── cpp.exp
│ ├── cpp.iobj
│ ├── cpp.ipdb
│ ├── cpp.lib
│ ├── cpp.log
│ ├── cpp.pch
│ ├── cpp.pdb
│ ├── cpp.pyd
│ ├── cpp.pyd.recipe
│ ├── cpp.tlog
│ ├── cpp.vcxproj.FileListAbsolute.txt
│ ├── dllmain.obj
│ ├── pch.obj
│ ├── python3.dll
│ ├── python311.dll
│ ├── vc142.pdb
│ ├── vcpkg.applocal.log
│ └── zlib1.dll
└── python
└── main.py
Pythonからの呼び出し
.pyd
が作成されたことを確認できたら、そこにsys.pathを通して読み込むサンプルコードを書いてみます。
import sys
sys.path.append("./../cpp/x64/Release")
try:
import cpp
except ImportError as e:
msg = "C++のdllモジュールが見つかりませんでした。ビルドが正しく行われているか確認してください。"
raise RuntimeError(msg) from e
if __name__ == '__main__':
print(cpp.add(1, 2))
print(help(cpp))
依存関係を明示的にするためにsys.pathで適当にPYTHONPATHを拡張してしまっていますが、本番で使う際にはC++ビルド時の設定でコピーする、IDEにPYTHONPATHを追加するなど適切に対処する方が良いです。
実行すると出力は以下のようになります。
C:\Python311\python.exe c:\sample-python-cpp\python\main.py
# >>> 3
# >>> Help on module cpp:
# >>>
# >>> NAME
# >>> cpp - pybind11 example plugin
# >>>
# >>> FUNCTIONS
# >>> add(...) method of builtins.PyCapsule instance
# >>> add(arg0: int, arg1: int) -> int
# >>>
# >>> A function that adds two numbers
# >>>
# >>> FILE
# >>> c:\sample-python-cpp\cpp\x64\release\cpp.pyd
# >>>
# >>>
# >>> None
これでPythonからdllが呼び出せました。
コメント