環境構築編に続き、デバッグの方法を説明します。
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. トラブルシューティング
(今後追記予定)
参考
以下を参考にさせていただきました。