Pybind11でC++関数をPythonから実行する（Windows & Visual Studio Codeな人向けの環境構築）

**（2021年1月追記）
以下の記事ではg++を使った環境構築・ビルド方法を説明していますが、msvcを使った[環境構築・ビルド方法](https://qiita.com/k_maki/items/01d425c06f990e34870d)および[デバッグ方法](https://qiita.com/k_maki/items/75bf05e4159be92c0bd9)の記事を投稿しましたのでそちらをご覧ください。g++を使ったデバッグはブレークポイントで止められなかったのであきらめましたorz**

---

Pybind11を使ってC++の関数をPythonから呼び出せるようにする環境を構築する方法を説明します。WindowsかつVisual Studio Codeを使っている人向けの記事があまりなかったので、いろいろ調べたことをまとめておきます。ただ、エディターはVisual Studio Codeでなくても大丈夫です。

# 0. 環境
以下の環境で確認しています。CMake以下のインストール方法はこの記事で説明します。

- OS: Windows 10
- エディター: Visual Studio Code 
- Python: 3.7 (Anaconda 3 2019.7)
- この記事でインストールするもの
  - CMake: 3.17.2
  - Mingw-w64: x86_64-8.1.0-posix-seh-rt_v6-rev0
  - pybind11: 2.5.0

# 1. 環境構築
## 1.1. CMakeのインストール
[CMake](https://cmake.org/)とは、C++等をビルドする際の設定を様々なコンパイラーで利用できるようにするツールです。が、私自身よく分かっていないので、詳しくは[こちら](https://kamino.hatenablog.com/entry/cmake_tutorial1)をみてください。

[ダウンロード ページ](https://cmake.org/download/)からWindows win64-x64 Installerをダウンロードし、インストールします。

## 1.2. Mingw-w64のインストール
[Mingw-w64](http://mingw-w64.org/doku.php)とは、フリーのC++コンパイラg++のWindwos 64bit版です。これも私自身よく分かっていないので、グーグル先生に聞いてみてください。

[ダウンロードページ](https://sourceforge.net/projects/mingw-w64/files/)からインストーラー等をダウンロードします。オンライン環境であれば、インストーラー`MinGW-W64-install.exe`をダウンロードしインストールします。オフライン環境であれば、7z形式のファイルをダウンロードして展開します。

以下、この記事では`g++.exe`の配置が次のようになっているとします:`D:\UserProgram\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\g++.exe`

次に、上記フォルダーを環境変数Pathに追加します。Powershellで追加するには、

```powershell:ユーザー単位
> $envPath = [System.Environment]::GetEnvironmentVariable("Path", "User")
> $envPath += ";D:\UserProgram\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin" #←g++.exeが含まれているパス
> [System.Environment]::SetEnvironmentVariable("Path", $envPath, "User")
```

端末単位で設定するには1行目と3行目の`"User"`を`"Macine"`に置き換えます。この場合、管理者権限が必要になります。

```powershell:端末単位（要管理者権限）
> $envPath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
> $envPath += ";D:\UserProgram\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin" #←g++.exeが含まれているパス
> [System.Environment]::SetEnvironmentVariable("Path", $envPath, "Machine")
```

## 1.3. Pybind11のインストール
オンライン環境なら、`pip`でインストールします。

```powershell:Powershell
> pip install pybind11
```

オフライン環境の場合は、
[PyPIのダウンロード ページ](https://pypi.org/project/pybind11/#files)から`pybind11-x.y.z-py2.py3-none-any.wh`(x, y, zは数字)をダウンロードしてから、`pip`でインストールします。

```powershell:Powershell
> pip install ダウンロード先/pybind11-2.5.0-py2.py3-none-any.whl  #2.5.0部分は適宜読み替え 
```

後で使うので、公式のサンプルもダウンロードします。オンライン環境（かつGitをインストール済み）なら、Powershell上でダウンロードしたい場所まで移動し、

```powershell:Powershell
git clone --recursive https://github.com/pybind/cmake_example.git
```

としてダウンロードします。オフライン環境の場合は、[公式のサンプル](https://github.com/pybind/cmake_example)の`Clone or download`ボタンを押します。~~また、[pybind11フォルダー](https://github.com/pybind/pybind11)も同様にダウンロードします。~~

~~**【重要】`cmake_example/pybind11/include/pybind11`以下のファイルを、`C:\ProgramData\Anaconda3\include\pybind11`以下のファイルで置き換えます。**置き換える前のファイルは古いみたいで、コンパイルは通りますが、`warning: 'void PyThread_delete_key_value(int)' is deprecated [-Wdeprecated-declarations]`というワーニングが大量に出ます。~~（2021/1修正。現時点ではワーニングが出ないため削除。）


# 2. やってみるさ（ ■ 一 ■）
先ほどダウンロードした`cmake_exmple`フォルダーから、`pybind11`、`src`、`CMakeLists.txt`を適当なフォルダー（以下、`project_root`とします）にコピーします。なお、パス名に日本語が含まれるとConfigureとGenarateができないため、半角英数字（スペースと記号もOK）にしてください。

```:フォルダー構成
project_root
├ pybind11
│ ├ docs
│ ├ include
│ │ └pybind11   
│ │  └・・・    
│ └・・・
├ src
│ └main.cpp
└ CMakeLists.txt
```

コンパイル エラー（なぜかはわからない）になるため、`main.cpp`の`#ifdef VERSION_INFO`～`#endif`部分をコメントアウトor削除。

```cpp:project_root/src/main.cpp
#include <pybind11/pybind11.h>

int add(int i, int j) {
    return i + j;
}

namespace py = pybind11;

PYBIND11_MODULE(cmake_example, m) {
    (略)
    m.def("add", &add, R"pbdoc(
        Add two numbers

        Some other explanation about the add function.
    )pbdoc");
   (略)
//コンパイルエラー（なぜかはわからない）になるので以下をコメントアウトor削除
//#ifdef VERSION_INFO
//    m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
//#else
//    m.attr("__version__") = "dev";
//#endif
}
```

```cmake:project_root/CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)
project(cmake_example)

add_subdirectory(pybind11)
pybind11_add_module(cmake_example src/main.cpp)
```

`project_root`をVisual Studio Codeで開き、`Ctrl+@` でターミナルに移動し、以下を実行します。

```Powershell:Powershell
> mkdir build                      # ビルド先のフォルダーを作成
> cd build                         # ビルド先に移動
> cmake -G "MinGW Makefiles" ..    # ConfigureとGenarate
> cmake --build .                  # ビルド
```

成功すれば、`build`フォルダーに`cmake_example.cp37-win_amd64.pyd`が作成されます。先ほどのターミナルで試してみましょう。

```Powershell:Powershell
> python           # Pythonを起動
>>> from cmake_example import add 
>>> add(1,2)
3
>>> exit()         # Powershellに戻る
```

`add`関数を呼び出せていれば成功です。

# 3. トラブルシューティング
## 3.1. ConfigureとGenarate
### 3.1.1. `cmake -G "MinGW Makefiles" ..`ができない
パス名に日本語が含まれるとConfigureとGenarateができません。パス名は大人しく半角英数字（スペースと記号もOK）にしましょう。日本語に対応して欲しいですね。

## 3.2. ビルド時
### ~~3.2.1. warning: 'void PyThread_delete_key_value(int)' is deprecated [-Wdeprecated-declarations]~~
~~上で書きましたが、`cmake_example/pybind11/include/pybind11`以下のファイルを、`C:\ProgramData\Anaconda3\include\pybind11`以下のファイルで置き換えます。置き換える前のファイルは古いみたいで、コンパイルは通りますが、`warning: 'void PyThread_delete_key_value(int)' is deprecated [-Wdeprecated-declarations]`というワーニングが大量に出ます。~~（2021/1修正。現時点ではワーニングが出ないため削除。）

### 3.2.2. error: '::hypot' has not been declared
2018年12月以前にリリースされたPythonの各バージョンを使っていると、このエラーでビルドが失敗します。原因はpyconfig.hで、次のように修正します。

```cpp:Pythonインストール先/include/pyconfig.h
  #define COMPILER "[gcc]"
- #define hypot _hypot     ←この行を削除する。
  #define PY_LONG_LONG long long
  #define PY_LLONG_MIN LLONG_MIN
  #define PY_LLONG_MAX LLONG_MAX
```

この問題は[公式でも議論](https://bugs.python.org/issue11566)されており、[プルリクエスト](https://github.com/python/cpython/pull/880)後、マージされています（実際の[コミット](https://github.com/python/cpython/commit/87667c54c6650751c5d7bf7b9e465c8c4af45f71)）。

# 4. おわりに
最後まで読んでいただき、ありがとうございます。
この記事では公式のサンプルを使っただけでしたが、他の人の記事を参考に、`main.cpp`や`CMakeLists.txt`を修正してみてください。
C++に疎いため、たったこれだけの事が出来るようになるまでに時間がかかりました。これからC++デビューしたいと思います。あと、VSCodeの拡張機能CMake Toolsの使い方もそのうち調べて記事にしたいです。


# 参考
以下の記事を参考にさせていただきました。
## Mingw-w64のインストール方法
- [Windows MinGW開発環境](https://www.torutk.com/projects/swe/wiki/Windows_MinGW開発環境)
- [【簡単】MinGW-w64のインストール方法【画像多め】](https://yukblog.net/mingw-w64-install/)


## CMakeの解説
- [勝手に作るCMake入門](https://kamino.hatenablog.com/entry/cmake_tutorial1)
- [Cmakeの使い方（その1）](https://qiita.com/shohirose/items/45fb49c6b429e8b204ac#cmake%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B)
- [CMakeを使ってみた（2）もう少しまともなプロジェクト](https://www.wagavulin.jp/entry/2011/11/27/222642)

## Pybind11の使い方
- [公式のサンプル](https://github.com/pybind/cmake_example)
- [pybind11でC++の関数をpythonから使う](https://qiita.com/ignis_fatuus/items/c7523c0fe2bc2f415d50)
- [pybind11を使ってPythonからC++コードを実行する方法](https://myenigma.hatenablog.com/entry/2016/12/17/075812)