前回はSynologyのNASをGitサーバにしたことを紹介しました。
今回はからは再びVisual Studio Code(VS Code)とWSL2を試してみたいと思います。
以前はWSL2+VS CodeでC言語を使ってみましたが、今回はC++を使ってみたいと思います。
環境の確認
今回はWSL2を使って導入したLinux Mintを利用しています。
Linux MintはUbuntuとほぼ互換性があるので、Ubuntuでも同様のことができると思います。
また、WindowsにVS Codeと「Remote – WSL」拡張機能を導入して、WSL2のLinuxと連携がとれるようにしています。
これは以前、C言語のために構築したものと同様です。
今回はこの環境でC++を使ってみようと言うことになります。
拡張機能としては「Remote – WSL」のほかに「C/C++拡張機能」が導入されていることを前提としています。
なお正直に白状すると、元ネタは下記の公式ブログだったりします。
ツールチェーンの導入
まずWSLのLinuxにログインして、C++をコンパイル・デバッグするためのパッケージ一式を導入しましょう。
下記のコマンドを実行すればOKです。
$ sudo apt-get update $ sudo apt-get install build-essential gdb
これでコンパイラ(g++コマンド)とデバッガ(gdbコマンド)が利用できるようになったはずです。
ソースコードの準備
まずWSL2のLinuxでソースコードを格納するディレクトリを作成しましょう。
今回はホームディレクトリにprojectsというディレクトリを作成し、その中に今回のテスト用にhelloworldというディレクトリを作成します。
$ cd ~ $ mkdir –p projects/helloworld $ cd projects/helloworld
次にこのディレクトリをWindows上のVS Codeで開きます。
WSL2側からVS Codeを起動する方法もありますが、今回は素直にWindows側でVS Codeを起動します。
そしてリモートエクスプローラで「WSL Targets」を選ぶと、インストールしているWSL2のディストリビューションが表示されているはずです。
そしてそのディストリビューションを右クリックして「Connect to WSL」を選択します。
これで新しいVS Codeのウィンドウが開き、左下の接続状態を示すエリアに「WSL: ディストリビューション名」と表示されます。
あとは、VS Codeのメニューの「ファイル」→「フォルダーを開く」を選択し、先ほど作成したディレクトリを入力して「OK」を選択します。
これでエクスプローラエリアに「HELLOWORLD [WSL:ディストリビューション名]」と表示され、WSL2のLinuxのフォルダが開かれたことがわかります。
次にこのフォルダ部分にマウスポインタを持って行き「新しいファイル」で「helloworld.cpp」というファイルを作成しましょう。
ここで「C/C++拡張機能」をオススメされた場合(=まだインストールしていない場合)は、インストールしましょう。
次に作成したhelloworld.cppに次のようなソースコードを入力して保存しましょう。
#include <iostream> #include <vector> #include <string> using namespace std; int main() { vector<string> msg {"Hello", "C++", "World", "from", "VS Code", "and the C++ extension!"}; for (const string& word : msg) { cout << word << " "; } cout << endl; return 0; }
VS Codeの画面は次のようになります。
いろいろ色がついているのは拡張機能を入れているためです。
VS Codeはソースコードの入力を保持してくれるIntelliSenseという機能に対応しています。
例えば、10行目にmsgに続いて「.」を入力すると、利用できるメンバ関数の一覧が表示され、上下キーで選択・タブキーで入力することができます。
上下キーで選択するだけでなく、メンバ関数名を入力していくと、候補が絞り込まれるので、選択がしやすくなります。
またサイドにそのメンバ関数の説明も表示されます(この画面では横幅が狭いため、左側にメンバ関数の説明が表示されます)。
このようにVS Codeを使うと効率的にコーディングすることができます。
helloworld.cppのビルド(コンパイル)と実行
VS Codeではビルド時の処理はtasks.jsonというファイルの記述します。
従って、手順は
- helloworld.cppをビルドするためのtasks.jsonの作成
- tasks.jsonに従いタスクの実行(ビルド)
- ビルドで生成されたファイルの実行
tasks.jsonの作成
このファイルの作成にはまず「ターミナル」→「既存のビルドタスクの構成」を選択します。すると使用するタスクの選択肢が表示されるので「C/C++: g++アクティブなファイルのビルド (コンパイラ: /usr/bin/g++)」を選択します。
これでtasks.jsonというファイルが.vscodeフォルダに作成されます。また、このファイルは自動的にエディタで開かれます。
生成されたtasks.jsonは私の場合は次のようになりました。
{ "version": "2.0.0", "tasks": [ { "type": "cppbuild", "label": "C/C++: g++ アクティブなファイルのビルド", "command": "/usr/bin/g++", "args": [ "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ], "options": { "cwd": "${fileDirname}" }, "problemMatcher": [ "$gcc" ], "group": { "kind": "build", "isDefault": true }, "detail": "コンパイラ: /usr/bin/g++" } ] }
重要なキーは次の通りです。
- command
このタスクで実行されるコマンド。この場合は「g++」になります。 - args
コマンド(g++)に渡す引数です。ここで記載した順番で引数は渡されるので記述する順番も重要です。
この例では、アクティブなファイル(${file})がコンパイラ(g++)に渡され、カレントディレクトリ(${fileDirname})にアクティブなファイル名から拡張子をのぞいた名前(${fileBasenameNoExtension})で実行ファイルが生成されます。
従って、helloworld.cppに対してこのタスクを実行すると、g++によりコンパイルされhelloworldという実行ファイルが生成されます。 - label
この設定を識別するための名前です。自由に変更可能です。 - isDefault
この値がtrueだとCtrl+Shift+Bのショートカット(あるいは「ターミナル」→「ビルドタスクの実行」)で、このタスクが実行されるようになります。
なお、値がfalseの場合でも、「ターミナル」→「タスクの実行」で表示されるタスクから選択することでこのタスクを実行可能です。
今回用意したhelloworld.cppをコンパイルするためには、このままで良さそうです。
タスクの実行
tasks.jsonファイルで定義されたタスクを実行するためには、helloworld.cppをエディタで表示させている状態でCtrl+Shift+Bをするか、「ターミナル」→「タスクの実行」を選択します。
するとタスクが実行され、VS Codeのターミナルエリアに実行状況が表示されます。
今回は「ビルドが正常に完了しました」となっているので、コンパイルエラーが発生しなかったことがわかります。
コンパイルエラーが発生した場合は、内容を確認し、エラーがなくなるまでソースコードを修正することになります。
また、エクスプローラ領域では「helloworld」というファイルが増えていることがわかります。これがビルドにより作成された実行ファイルになります。
helloworldの実行
それではビルドしてできたhelloworldを実行してみましょう。
まずターミナル領域の「+」を選択してみましょう。
これでWSL上のBashによるターミナルが開きますので「./helloworld」と入力すれば実行されます。
helloworld.cppのデバッグ
ビルドができたら次はデバッグをしてみます。
VS Codeではデバッグ(実行)時の処理はlaunch.jsonというファイルの記述します。
従って、手順は
- helloworld.cppをデバッグするためのlaunch.jsonの作成
- launch.jsonに従いデバッグの開始
- VS Codeでデバッガの操作
となります。
launch.jsonの作成
このファイルもtasks.jsonと同様にVS Codeがテンプレートを生成してくれます。
「実行」→「構成の追加」を選び、表示された選択肢の中から「C++ (GDB/LLDB)」を選択しましょう。
これでlaunch.jsonが.vscodeディレクトリに作成されますが、このlaunch.jsonはほとんど中身がありません。
そこで右下に表示されている「構成の追加」ボタンを選択しましょう。 そして表示される選択肢の中から「C/C++: (gdb) 起動」を選択します。
これで次のようにlaunch.jsonにgdbを起動するための設定が追加されます。
ここで作成された設定は実はそのままでは動かず、いくつか修正する必要があります。
修正ポイントは次の点です。
- name
わかりやすい名前に変更する。 - program
ビルドして生成されたプログラムを実行するようにする。具体的には「${fileDirname}/${fileBasenameNoExtension}」とすることで、tasks.jsonで生成されたhelloworldを指定したことになります。 - preLaunchTask
このキーは生成されたlaunch.jsonにはないので追加することになります。
事前にtasks.jsonで定義したビルドが動くようにしておくと便利です。
これらの点を修正したlaunch.jsonは次のようになります。
{ // IntelliSense を使用して利用可能な属性を学べます。 // 既存の属性の説明をホバーして表示します。 // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "g++ アクティブファイルのビルドとデバッグ", "type": "cppdbg", "request": "launch", "program": "${fileDirname}/${fileBasenameNoExtension}", "args": [], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], "externalConsole": false, "MIMode": "gdb", "setupCommands": [ { "description": "gdb の再フォーマットを有効にする", "text": "-enable-pretty-printing", "ignoreFailures": true }, { "description": "逆アセンブリ フレーバーを Intel に設定", "text": "-gdb-set disassembly-flavor intel", "ignoreFailures": true } ], "preLaunchTask": "C/C++: g++ アクティブなファイルのビルド" } ] }
preLaunchTaskキーに指定しているのは、tasks.jsonで定義したビルドタスクのlabelになります。
プログラムの実行
それではプログラムを実行してみましょう。
まずhelloworld.cppをアクティブにした状態で、「実行」→「デバッグの開始」を選択します。F5でもOKです。
「helloworld.cpp」をアクティブにするのは忘れがちなので注意しましょう。
すると右下の「デバッグコンソール」にいろいろ表示が出て、最終的に「The program helloworld has exited with code 0 (helloworldプログラムは戻り値0で終了しました)」という結果になっています。
これではわかりにくいので「ターミナル」で出力を確認してみましょう。
すると「Hello C++ world form VS Code and the C++ extension!」と表示され、helloworld.cppが正常に動作したことがわかります。
デバッグしてみる
実は先ほどの「プログラムの実行」もデバッガ(gdb)で動作していたのですが、実行完了してしまいデバッグらしい操作をしていませんでした。
そこでいかにもデバッグらしい操作をしてみます。その準備としてブレークポイントを9行目に設定しておきます。
ブレークポイントは行番号の左となりをクリックすると設定することができます。設定したブレークポイントは赤丸で表示されます。
ブレークポイントが設定できたら、「実行」→「デバッグの開始」で再び実行してみます
するとブレークポイントを設定した9行目がハイライトされ、ここで処理が止まったこと(ブレークしたこと)がわかります。
また、画面上部には実行状態を制御するためのパネルが表示されます。
このパネルは左端をドラッグすると自由に動かせます。
ステップ実行
まずステップ実行をしてみます。
ステップ実行には、次の行に進むステップオーバー、関数があった場合に関数内に入っていくステップイン、関数を脱出するステップアウト、があります。
デバッグ用のパネルの左から2番目のボタンを選択しましょう。これがステップオーバーになります。F10キーでも同様です。
するとハイライトが11行目に進みます。
このとき右側の「変数」を確認してみると次のようになっています。
変数msgはvectorなので、0~5の6つの文字列が格納されていることがわかります。
一方、for文はまだ実行していないので、for文で利用される変数wordや、for文を制御するためにコンパイラによって生成された変数__for_rangeと変数__for_begin, 変数__for_endは正常に表示されていません。
ここでもう一度ステップオーバーを実行すると、ハイライトは13行目(coutの行)へ進み、変数は次のように変ります。
for文のループ範囲を示す変数__for_rangeには変数msgの値がセットされ、変数wordの値はその最初の要素である”hello”になっています。
このようにステップ実行や表示される変数の値を駆使することで、プログラムが意図通りに動作しているのかを確認していくことになります。
変数のウォッチ
プログラムの実行中に特定の変数の値を確認し続けたいケースもあると思います。このような場合に便利なのがウォッチです。
ウォッチする変数を追加するには「ウォッチ式」のエリアの「+」を選択します。
今回はforループの中で利用されている変数wordをウォッチしてみます。
プログラムの実行前では、ウォッチに変数wordをついかしても「使用不可」と表示されます。
この状態でデバッグを開始してみます。9行目でブレークした時点では変数wordはまだスコープ外なので値は表示されていません。
そのままステップオーバーを繰り返し、for文の中に入っていくとウォッチ式のエリアに表示されている変数wordの値が次々と変っていきます。
その他の変数確認方法
実はVS Codeにはもっと簡単に変数の値を確認する方法が用意されています。
それはみたい変数の上にマウスカーソルを持って行くというものです。
例えば、for文の内部をステップ実行している状態で変数wordにカーソルを乗せてみます。
すると変数の値が表示されます。
変数msgにマウスカーソルを合わせると、vectorの要素が表示されます。
これはかなりお手軽で便利ではないかと思います。
C/C++拡張機能のカスタマイズ
これまでの説明で、「VS Code」+「Remote – WSL拡張機能」+「C/C++拡張機能」という組み合わせで、C++のコーディング・ビルド・デバッグができました。
ただ、C++の対応規格などを変更したいなど、細かい設定をしたいという場合もあると思います。
そのような場合は、VS Codeのコマンドパレットで「C++」と入力して表示されるコマンドから、「構成の編集 (UI)」を選ぶとカスタマイズすることができます。
これでエディタに「C/C++ Configurations」というタブが開き、C/C++拡張機能設定項目が表示されます。
例えば、C++の言語規格として「ISO 2011 C++ Standard (いわゆるC++ 11)」を利用したい場合は、C++基準を「C++11」に変更すると良いでしょう。
これでIntelliSenseでの補完がC++11に従って行われるようになります。
ただ、これだけだとコンパイラに対する言語規格指定が行われないようです。tasks.jsonを開き、argsに「-std=c++11」を追加する必要があります。
変更したtasks.jsonのargs部分は次のようになります。
"args": [ "-std=c++11", "-fdiagnostics-color=always", "-g", "${file}", "-o", "${fileDirname}/${fileBasenameNoExtension}" ],
なおC/C++拡張機能の設定を変更すると、その内容は.vscodeディレクトリにc_cpp_properties.jsonというファイルで保存されます。
このように.vscodeディレクトリには作成しているプログラムに関する様々な情報が保持されるので、ソースコードをコピーする際には.vscodeディレクトリも一緒にコピーしておくと設定も維持されるので便利と思います。
まとめ
今回はVisual Studio Code + WSL2という環境でC++のコーディング・ビルド・デバッグを試してみました。
多少は手で設定する項目は有りますが、VS Codeによってビジュアルでデバッグできるのはかなり使いやすいのではないかと思います。
次回はWSL2+VS Codeという環境でOpenCV(C++)を使ってみたいと思います。
コメント