環境構築編に続き、デバッグの方法を説明します。
1. 呼び出し用スクリプトの準備
ビルドが終わった段階のフォルダーのルートに、main.pyを追加します。
project_root
├ build
│ └ Debug
│ └ cmake_example.cp37-win_amd64.pyd
├ pybind11
├ src
│ └main.cpp
├ CMakeLists.txt
└ main.py ←これを追加
main.pyはC++関数を呼び出すスクリプトで、内容は次のとおり:
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が開かれる:
{
// 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}"を入力する:
{
"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.cのreturn 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に次が追加されます:
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "(Windows) 接続",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
}
3.2. デバッグの実行
main.pyのa = add(1,2)、main.cのreturn 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を準備します。
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ファイルを置き、リボンxlwingsのImport Functionsをクリックし、VBエディター(Alt+F11)の参照設定でxlwingsにチェックを入れます。適当なセルでxw_add関数が使えれば、呼び出し元Excelファイルの準備はOKです。(xlwingsの詳しい説明はこちらをご覧ください。)
(xlwingsは私の推しメンなんですが、残念なことにopenpyxlに比べて知名度がいまいちです(本が売っていない!)。これを機に是非使ってみてください。ドキュメントもしょぼいですが日本語化されています。)
5.2. デバッグの実行
ブレークポイントは以下の3か所に設定します:
- VBA: 標準モジュール
xlwings_udfs内のどこか - Python:
main_xw.pyのreturn add(int(a), int(b)) - C++:
main.cのreturn i + j;
デバッグを実行しましょう。3.2.と同様に、main.pyを開き、Python: Current Fileを実行し、(Windows) 接続でアタッチします。xlwingsのデバッグ用のサーバーも立ち上がっているので、ExcelのxliwngsリボンでDebug UDFsにチェックを入れれば準備完了です。
適当なセルに=xw_add(1,2)と入力すれば、VBAからPythonに、PythonからC++にブレークポイントを移動しながら実行することができます:

6. トラブルシューティング
(今後追記予定)
参考
以下を参考にさせていただきました。