Pybind11でC++関数をPythonから実行する(Windows & Visual Studio Codeな人向け) デバッグ編


環境構築編に続き、デバッグの方法を説明します。

1. 呼び出し用スクリプトの準備

ビルドが終わった段階のフォルダーのルートに、main.pyを追加します。

フォルダー構成
project_root
├ build
│ └ Debug
│   └ cmake_example.cp37-win_amd64.pyd
├ pybind11
├ src
│ └main.cpp
├ CMakeLists.txt
└ main.py              ←これを追加

main.pyはC++関数を呼び出すスクリプトで、内容は次のとおり:

main.py
import os
from build.Debug.cmake_example import add


print(f'PID: {os.getpid()}') # アタッチしてデバッグするためのもの

a = add(1,2)
print(a)

2. C++拡張のデバッグ

はじめにC++関数部分のみのデバッグ方法を説明します。ブレークポイントを設定できるのはC++のみです。

2.1. デバッグ用の構成の作成

コマンドパレット(Ctrl+p)からDebug: Open launch.jsonと入力し、C++ (Windows)を選択。launch.jsonが開かれる:

launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(Windows) 起動",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "プログラム名を入力してください (例: ${workspaceFolder}/a.exe)",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false
        }
    ]
} 

上記を修正。"program"部分にPythonのパスを、"args"の括弧内に"${file}"を入力する:

launch.json(修正例)
        {
            "name": "(Windows) 起動",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "c:/programdata/anaconda3/python.exe", //ここと
            "args": ["${file}"],                      //ここ
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false
        }

注意: "stopAtEntry"の値はfalseのままにすること。trueにすると、Pythonの実行ファイル(python.exe)をデバッグしようとしてしまい、Unable to open 'python.c'というエラーで止まるため。

2.2. デバッグの実行

main.creturn i + j;の部分にブレークポイントを設定し、main.pyを開いた状態でデバッグを実行(F5)します。問題なければブレークポイントで止まるはずです。

3. PythonとC++拡張の混合モードでのデバッグ

今度はPythonとC++拡張の同時デバッグ方法を説明しす。PythonとC++の両方にブレークポイントを設定できます。

Pythonのデバッガーを起動して、そこにC++のデバッガーをアタッチすることで実現します。こちらで非常にわかりやすく解説されていますので、是非ご覧ください。

3.1. デバッグ用の構成の作成

Python用、C++用の二つの構成を追加します。

  • C++: コマンドパレットから、Debug: Select and Start Debugging->構成の追加...->C/C++: (Windows) 接続で追加
  • Python: main.pyを開いて、コマンドパレットからDebug: Select and Start Debugging->構成の追加...->Python->Python File で追加

launch.jsonに次が追加されます:

launch.json
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "console": "integratedTerminal"
        },
        {
            "name": "(Windows) 接続",
            "type": "cppvsdbg",
            "request": "attach",
            "processId": "${command:pickProcess}"
        }

3.2. デバッグの実行

main.pya = add(1,2)main.creturn i + j;にブレークポイントを設定します。
デバッグを実行しましょう。まずはPythonから。main.pyを開き、コマンドパレット->Debug: Select and Start Debugging->Python: Current Fileで実行すると、先ほどのブレークポイントで止まります。また、ターミナルにプロセスのIDがPID: xxxxと表示されます(xxxxは数字)。
次にC++。コマンドパレット->Debug: Select and Start Debugging->(Windows) 接続で開始します。"アッタッチするプロセスを選択する"と表示されますので、先ほどのPIDを入力すれば準備完了です。
main.pyに戻り、ステップ実行(F10)すればC++のブレークポイントで止まります。

4. (おまけ1)Jupyterとの連携

main.pyと同様の内容を.ipynbファイルで実行し、そのPIDにアタッチすれば、JupyterとC++のデバッグを連携できます。これは便利かも。

5. (おまけ2)Excel VBAとPythonとC++拡張の混合デバッグ

ExcelからPythonを呼び出して、そのPythonからC++を呼び出すことがあるかもしれません。各種設定をExcelのシートに入力して、そこからPythonを呼び出して計算して出力するけれど、計算負荷が高そうな(or 既存のC++資産を活かしたい)時にC++を使うという感じで。Excelはフロントエンド、Pythonは全体的な処理と出力、C++は計算を担当する使い方です。

そんんことしねーよという声も聞こえてきましたが、引き続き今回のコードで説明します。

5.1. Excelファイルの準備等

Excel VBAから利用&デバッグ可能な次のファイルmain_xw.pyを準備します。

main_xw.py
import os
from build.Debug.cmake_example import add
import xlwings as xw


@xw.func
def xw_add(a, b):
    return add(int(a), int(b))


if __name__ == '__main__':
    print(f'PID: {os.getpid()}')
    xw.serve()

Excel VBAとの連携にはxlwingsを使用します。同じフォルダーにmain_xw.xlsmファイルを置き、リボンxlwingsImport Functionsをクリックし、VBエディター(Alt+F11)の参照設定でxlwingsにチェックを入れます。適当なセルでxw_add関数が使えれば、呼び出し元Excelファイルの準備はOKです。(xlwingsの詳しい説明はこちらをご覧ください。)

(xlwingsは私の推しメンなんですが、残念なことにopenpyxlに比べて知名度がいまいちです(本が売っていない!)。これを機に是非使ってみてください。ドキュメントもしょぼいですが日本語化されています。)

5.2. デバッグの実行

ブレークポイントは以下の3か所に設定します:

  • VBA: 標準モジュールxlwings_udfs内のどこか
  • Python: main_xw.pyreturn add(int(a), int(b))
  • C++: main.creturn i + j;

デバッグを実行しましょう。3.2.と同様に、main.pyを開き、Python: Current Fileを実行し、(Windows) 接続でアタッチします。xlwingsのデバッグ用のサーバーも立ち上がっているので、ExcelのxliwngsリボンでDebug UDFsにチェックを入れれば準備完了です。

適当なセルに=xw_add(1,2)と入力すれば、VBAからPythonに、PythonからC++にブレークポイントを移動しながら実行することができます:

6. トラブルシューティング

(今後追記予定)

参考

以下を参考にさせていただきました。

デバッグ方法