C++をPythonから呼び出す

C++をPythonから呼び出す

WindowsでC++で作成したdll(so)をPythonから呼び出す機会があったので、備忘録を兼ねてまとめておきます。

2024/04/21現在、C++をPythonから呼び出す主な方法はboost pythonpybind11があるようです。

本記事ではWindows + Visual Studio 2019 でpybind11を用いてPythonからの呼び出しを試してみます。
動作コードはgithubに置いているため、良ければ参考にしてください。

初めに

Visual studioでソリューションとプロジェクトの作成

Visual studioでソリューションとビルド用プロジェクトを作成しておきます。
C++の空のプロジェクトで適当な名前で作成します。今回はcppにしました。
cpp

以下のファイルが作成されるはずなので、ビルドが通ることを確認します。
folder_structure

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ディレクトリも追加します。以下のような設定になります。
構成はReleasex64にしていますが、この辺りは使用するPythonに合わせてください。
include_directory

ターゲットファイルの拡張子の変更

ターゲットファイルの拡張子を.dllから.pydに変更しておきます。
これはPythonからimport cppで直接読めるようにするためですが、cdll.LoadLibraryを用いても読み込めるため、好みの問題にはなります。

target_file

ビルドの確認

上記の設定を加え、ビルドが正しくできるか確認します。成功した場合、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が呼び出せました。

コメント

タイトルとURLをコピーしました