Visual Studio Code で C++ 開発 with Windows Subsystem for Linux (OS は Windows 10 ですよ)

最近にわかに Visual Studio Code (VSCode) のお勉強をしています。

以前の記事 WSL を使って Windows 10 の上で Linux を動かす - ある日録Windows Subsystem for Linux (WSL)と、その上に Ubuntu を入れたので、VSCode と WSL を使ってC++のビルドとかデバッガとか IntelliSense とか使ってみる。そのための設定。

以下超ざっくり。

VSCode インストール

まあ、これはググればすぐ出来ます。

統合ターミナルを WSL にする

Ctrl-Shift-P Select Default Shell で WSL を選ぶ。

↓に細かい話あり。

Integrated Terminal in Visual Studio Code

とりあえずこれで、Ctrl+@ で統合ターミナルを表示しておけば、VSCode だけで、編集してコンパイルはできる。

g++ -g -Wall source.cpp -o source

とかターミナルでコマンドを打てばよい。

C/C++ extension のインストール

でもまあ、せっかくやるなら、IntelliSenseも使いたいし、デバッガでステップ実行もしたいので、その辺の機能が入った extension を入れる。

左の列の機能拡張ボタンを押すか Ctrl+Shift+X で extension のリストを表示。小窓に c/c++ と入力して検索する。 インストールして、再読み込みすればOK。

C++ programming with Visual Studio Code

ひととおり読んだ方がいいかもしれないが、あまり読んでない。

システムヘッダへのPATH を intelliSense に教えてあげる。

  • c_cpp_properties.json に WSL コンフィグレーションを追加

下記サイトの記述を参考に書く。

vscode-cpptools/Windows Subsystem for Linux.md at master · Microsoft/vscode-cpptools · GitHub

リンク先ではディストリビューションUbuntu 前提で書かれている。WSL上に他のディストリビューションを入れている場合は変更が必要。(そのことも書いてある)。

あと、最後に、一言、WindowsからWSLのファイル書き換えたら酷いことになるよ。的な記載へのリンクがあった気がする。UbuntuとかのファイルがWindowsからたどれるからといって、くれぐれも書き換えないようにしよう。

  • Ctrl+Shift+P c_cpp_configration.json と打ち込み、c_cpp_configuration.json を開く。
  • 以下の WSL (Ubuntuの場合) の Configuration を c_cpp_configuration.json に書く。

          {
              "name": "WSL",
              "intelliSenseMode": "clang-x64",
              "includePath": [
                  "${workspaceRoot}",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/c++/5",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/x86_64-linux-gnu/c++/5",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/c++/5/backward",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/lib/gcc/x86_64-linux-gnu/5/include",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/local/include",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/x86_64-linux-gnu",
                  "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include"
              ],
              "defines": [
                  "__linux__",
                  "__x86_64__"
              ],
              "browse": {
                  "path": [
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/c++/5",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/x86_64-linux-gnu/c++/5",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/lib/gcc/x86_64-linux-gnu/5/include",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/local/include",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/x86_64-linux-gnu",
                      "${localappdata}/Packages/CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc/LocalState/rootfs/usr/include/*"
                  ],
                  "limitSymbolsToIncludedHeaders": true,
                  "databaseFilename": ""
              },
              "cStandard": "c11",
              "cppStandard": "c++17"
          },
    
  • コマンドパレット> c/cpp select configuration で WSL を選ぶ

  • VSCode を再起動すると選択しなおす必要がある件は調査必要
    → と思ったが、c_cpp_properties.json の一番上に書いたらうごいた。Win32 より前にあればいいのか? WSLのコンフィグレーションを Win32 のコンフィグレーションの下に書いてしまうと、次回 VSCode の起動時に Win32 のほうが有効になってしまう。 Select Configuration で選択しているのに。。。そのうち直るかな。

コンパイル

tasks.json を書く。けっこう簡単。

  • tasks.json は Ctrl+Shift+P Configure task とかして、タスクを選ぶと開ける。
  • メニュー>タスク>タスクの構成 でも同じ。

              {
                  "label": "build",
                  "type": "shell",
                  "command": "g++ -g -Wall -O0 VSCodeTest.cpp",
                  "problemMatcher": [
                      "$gcc"
                  ],
                  "group": {
                      "kind": "build",
                      "isDefault": true
                  }
              }
    
  • g++ に -O0 を付けているのは最適化をオフしておかないとデバッグでステップ実行すると変な動きになるため。VisualStudio でやるみたいに DebugビルドとReleaseビルドを分けておいてもいいかもね。

  • isDefault を true にしておくと Ctrl+Shift+B でビルドできる。

実行

tasks.json を書く。これも同じく簡単。

            {
                "label": "run",
                "type": "shell",
                "command": "./a.out",
                "problemMatcher": []
            }

これでメニュー>タスクの実行>run を選ぶと Ubuntubash 上で a.out が実行される。

デバッガ

下記サイトを 参考に launch.json を書く。

https://github.com/Microsoft/vscode-cpptools/blob/master/Documentation/Debugger/gdb/Windows%20Subsystem%20for%20Linux.md

  • Ctrl+Shift+P open launch.json と打ち、launch.json を開く。(lanunch.json がないときどうなるだろう?)
  • launch.json を開くと右に 構成の追加 ボタンがあるので、これを押す。
  • C/C++: (gdb) Bash on Windows Launch を選ぶ
  • テンプレがインサートされるので、必要なとこだけ変更。たしか・・・
    • program
    • cwd
    • sourceFileMap
  • くらいだったような。

          {
              "name": "(gdb) Bash on Windows Launch",
              "type": "cppdbg",
              "request": "launch",
              "program": "/mnt/c/Code/VSCodeTest/a.out",
              "args": [],
              "stopAtEntry": false,
              "cwd": "/mnt/c/Code/VSCodeTest",
              "environment": [],
              "externalConsole": true,
              "sourceFileMap": {
                  "/mnt/c": "c:\\"
              },
              "pipeTransport": {
                  "debuggerPath": "/usr/bin/gdb",
                  "pipeProgram": "C:\\Windows\\sysnative\\bash.exe",
                  "pipeArgs": ["-c"],
                  "pipeCwd": ""
              },
              "setupCommands": [
                  {
                      "description": "Enable pretty-printing for gdb",
                      "text": "-enable-pretty-printing",
                      "ignoreFailures": true
                  }
              ]
          }
    
  • 左のデバッグアイコンを押してデバック用のペインを開いて、さっき作った構成を選択し右三角をぽちっとすれば実行可能。

  • ソースコードの行番号の左をクリックしてブレークポイントをはっておけばそこで止まる。
  • F10 でステップオーバー
  • F11 でステップイン

以上。

WSL を使って Windows 10 の上で Linux を動かす

Windows 10 で Linux を動かす

Windows10 は何回か大きなバージョンアップがあって、そのたびに Linux を動かす機能をすこしずつ強化してきた。

2016/08/24 の Aniversary Update で少し動くようになり、 2017/07/28 Creators Update でもっと動くようになったそう。

まだ完全ではないらしいがとりあえずそれ、入れてみた。ここにその軌跡を記す。

発端

キーボードをHHKB(http://www.pfu.fujitsu.com/hhkeyboard/)にしたので、家で emacs の練習しとくかあ。

と思い、gnupack を起動したら mintty がエラー。teraterm に付いてる cygterm も動かない。。。

Windows 10 の仮想デスクトップをいくつか使っているせいかなと思って デスクトップを一つだけにしたけど動かず。。。

どうしよ。emacs で編集して gccコンパイルして、とかやれなくなってしまった。

・・・そういえば、WindowsUbuntu 動かすとか、そんな話あったよなあ。

参考にしたサイト(ありがとうございます!)

Windows10 の "Bash on Ubuntu on Windows" で困ったこと - Qiita

Windows Subsystem for Linuxをインストールしてみよう! - Qiita

  • うちの Windows 10 は Creator Update からだいぶ経っていることもあってか、初めから Windows Subsystem for Linux がインストールされていた。
  • そのため、Microsoft Store から Ubuntu をインストールしただけで動いた。
  • Microsoft Store のレビューにありましたが、初心者が手を出すとクラッカーの攻撃に晒されるかもしれないんだそうです。ご注意ください。

Windows Sybsystem for Linux

Winodws の機能から、Windows Sybsystem for Linux にチェックを付ける。 (私のとこではもう付いてた) OKすると再起動するらしい。

f:id:nodamotoki:20180120220935p:plain
Windowsの機能

  • Windows の機能は、OptionalFeatures.exe を実行すると出ます。
  • 設定メニューからもいけます。けっこう深いとこにいるけど。
    • 設定>アプリ>アプリと機能>(右のほうの)プログラムと機能>(左のほうの)Windowsの機能の有効化または無効化

Microsoft Store から Ubuntu をインストール

f:id:nodamotoki:20180120221934p:plain
Microsoft Store

スタートにピン留めした Ubuntu から実行

実行は スタート>Ubuntu でOK

cmd.exe から bash を実行することでも起動する。

Microsot Store のレビューコメントを見ると PowerShell から実行した方が速い?とか書いてあったので、PowerShell から実行してみたり。

使いたいプログラムをインストール

参考サイトの記載をみながら、ダウンロード元を、北陸先端科学技術大学院大学さん(http://ftp.jaist.ac.jp)に変更。

update & upgrade するらしいのでやってみる

sudo apt update
sudo apt upgrade

これで、最新版になったのかな。

↓ apt コマンドはこちらで少しお勉強(ありがとうございます!)。

「apt-get」はもう古い?新しい「apt」コマンドを使ったUbuntuのパッケージ管理 | Linux Fan

それから emacs やら gcc やらをゲット

$ sudo apt install emacs
$ sudo apt install build-essential     # gcc
$ sudo apt install python ruby         # python と ruby 

すべてをやり直したいとき

lxrun /uninstall /full
lxrun /install

cmd.exe で上のコマンドを叩くとすべてが消えて無くなり、新しく入れ直されるのだとか。 (Windows Subsystem on Linux を消したり入れ直したりする古い?コマンドらしい)

そういえば最初に lxrun /uninstall /full をとりあえずやってしまったんだけど、

そのとき「保留中」みたいな表示が出てたなあ。

パソコンを再起動したら今日やったこと全部消えてしまうのかな。

以上!

#if 0 で囲まれた「文字」のほうの色を変える方法

はじめに

vim では、はじめから当たり前のように #if 0 をコメントと同じ色で表示してくれるのに Emacs ではそうはいかない。困ったもんだ。

一応 cpp-highlight-buffer を使うと「背景色」を変えるのは比較的簡単にできる。 (簡単と言ってもけっこう癖あるけど)

しかし、文字色を変える方法は、さらにわかりにくいので、ここにその方法を載せときます。

cpp-highlight-buffer の裏技を使う。

裏技といっても、Emacs の Face について知っていれば当たり前なのかも…。

というわけで Face を定義して cpp-highlight-buffer でその Face を使います。

.emacs に以下の設定を書くだけ。

;; cpp-highlight-buffer
(defface if-zero-face (quote ((t (:foreground "brightblack")))) nil)
(setq cpp-edit-list
      (quote
       (("0" if-zero-face default both)
        ("1" default if-zero-face both))))
(setq cpp-face-type (quote dark))
(setq cpp-known-face (quote default))
(setq cpp-unknown-face (quote default))
(add-hook 'c-mode-common-hook (lambda () (cpp-highlight-buffer t)))

やっていること

if-zero-face というフェイスを作る。フェイスは list-faces-display で列挙されるやつです。

ここでは foreground (文字色) を brightblack (黒だけど少しあかるい) にしてる。 自分の Emacs で使える色は list-colors-display で確認できるよ。

あとは cpp-highlight-buffer の設定。 #if 0 のときと、 #if 1 のときの #else 側のFace を if-zero-face と指定する。

その他 cpp-highlight-buffer の設定をいくつか。

フックを追加して c-mode とかのときに (cpp-highlight-buffer t) を実行するようにしておく。

defface で作る Face の設定を変えれば、背景色を変えたり bold, italic, underline なども指定できる。

以上

vimdiff の設定と使い方

これから仕事で vimdiff を使いそうなので情報収集。

cygwin だと vimdiff 単体では Setup.exe の検索には引っかからないけれど vim を入れると vimdiff もついてくる。

~/.vimrc 設定

set nocompatible

" 画面表示の設定

set number         " 行番号を表示する
set cursorline     " カーソル行の背景色を変える
"set cursorcolumn   " カーソル位置のカラムの背景色を変える
set laststatus=2   " ステータス行を常に表示
set cmdheight=2    " メッセージ表示欄を2行確保
set showmatch      " 対応する括弧を強調表示
set helpheight=999 " ヘルプを画面いっぱいに開く
"set list           " 不可視文字を表示
" 不可視文字の表示記号指定
"set listchars=tab:?\ ,eol:?,extends:?,precedes:?

" カーソル移動関連の設定

set backspace=indent,eol,start " Backspaceキーの影響範囲に制限を設けない
set whichwrap=b,s,h,l,<,>,[,]  " 行頭行末の左右移動で行をまたぐ
set scrolloff=8                " 上下8行の視界を確保
set sidescrolloff=16           " 左右スクロール時の視界を確保
set sidescroll=1               " 左右スクロールは一文字づつ行う

" ファイル処理関連の設定

set confirm    " 保存されていないファイルがあるときは終了前に保存確認
set hidden     " 保存されていないファイルがあるときでも別のファイルを開くことが出来る
set autoread   "外部でファイルに変更がされた場合は読みなおす
set nobackup   " ファイル保存時にバックアップファイルを作らない
set noswapfile " ファイル編集中にスワップファイルを作らない

" 検索/置換の設定

set hlsearch   " 検索文字列をハイライトする
set incsearch  " インクリメンタルサーチを行う
set ignorecase " 大文字と小文字を区別しない
set smartcase  " 大文字と小文字が混在した言葉で検索を行った場合に限り、大文字と小文字を区別する
set wrapscan   " 最後尾まで検索を終えたら次の検索で先頭に移る
set gdefault   " 置換の時 g オプションをデフォルトで有効にする

" タブ/インデントの設定

"set expandtab     " タブ入力を複数の空白入力に置き換える
set tabstop=4     " 画面上でタブ文字が占める幅
set shiftwidth=4  " 自動インデントでずれる幅
set softtabstop=4 " 連続した空白に対してタブキーやバックスペースキーでカーソルが動く幅
set autoindent    " 改行時に前の行のインデントを継続する
set smartindent   " 改行時に入力された行の末尾に合わせて次の行のインデントを増減する

" 動作環境との統合関連の設定

" OSのクリップボードをレジスタ指定無しで Yank, Put 出来るようにする
set clipboard=unnamed,unnamedplus
" マウスの入力を受け付ける
set mouse=a
" Windows でもパスの区切り文字を / にする
set shellslash
" インサートモードから抜けると自動的にIMEをオフにする
"set iminsert=2

" コマンドラインの設定

" コマンドラインモードでTABキーによるファイル名補完を有効にする
set wildmenu wildmode=list:longest,full " コマンドラインの履歴を10000件保存する
set history=10000

" ビープの設定

"ビープ音すべてを無効にする
set visualbell t_vb=
set noerrorbells "エラーメッセージの表示時にビープを鳴らさない


" vimdiffの色設定
highlight DiffAdd    cterm=bold ctermfg=10 ctermbg=22
highlight DiffDelete cterm=bold ctermfg=10 ctermbg=52
highlight DiffChange cterm=bold ctermfg=10 ctermbg=17
highlight DiffText   cterm=bold ctermfg=10 ctermbg=21

設定ネタ元

http://vimblog.hatenablog.com/entry/vimrc_set_recommended_options http://qiita.com/takaakikasai/items/b46a0b8c94e476e57e31

操作

vimdiff a.c b.c


[ c                 前の差分
] c                 次の差分

do                  他の窓の今の差分を、自分のほうへ取り込む(obtain)
dp                  自分の窓の今の差分を、他の窓へ押し込む(put)

ctrl+w w            窓を移動
ctrl+w ctrl+w       〃

ctrl+w h            左の窓のファイルへ移動
ctrl+w j            下の窓のファイルへ移動
ctrl+w k            上の窓のファイルへ移動
ctrl+w l            右の窓のファイルへ移動

ctrl+w H            ファイルの位置を左窓へ+横分割に変更
ctrl+w J            ファイルの位置を下窓へ+縦分割に変更
ctrl+w K            ファイルの位置を上窓へ+縦分割に変更
ctrl+w L            ファイルの位置を右窓へ+横分割に変更

ctrl+w [num] <      窓の仕切りを num 個左へ
ctrl+w [num] >      窓の仕切りを num 個右へ
ctrl+w [num] +      窓の仕切りを num 個上へ
ctrl+w [num] -      窓の仕切りを num 個下へ
ctrl+w =            窓の仕切りを戻す

:wqa                変更を全部ファイルに書き込んで修了
:qa!                変更を全部破棄して修了

操作ネタ元

https://ogatism.jp/vi_split_vimdiff/

ターミナルの Emacs で linum-mode にすると行番号とファイルの内容がくっついてしまう

フォーマットを指定すればいいだけだった。なんだ。簡単だなあ。

(global-linum-mode t)
(setq linum-format "%4d ")
  • %4d + スペース一つというフォーマットにした。

TeraTerm で動かしている Emacs でマウスを使えるようにする

マウストラッキング

TeraTerm などのターミナル上で動く(ホスト側の)アプリケーションは、マウスのカーソル位置などの情報をターミナルソフトに要求して受け取る機能がある。

TeraTerm はデフォルトでこの要求に答える設定になっているので、ホスト側で動くアプリケーションで、この機能(マウストラッキングというらしい)を有効にしておけば、ターミナルでもマウスが使える。

TeraTermのヘルプを見ると、もともとは xterm の機能なので、TERM環境変数が xterm 系でないと使えないのでは… と書いてあるが、Emacsは単独でこの機能のON/OFFが出来るのでたぶん大丈夫でしょう…。

Emacs の設定

(xterm-mouse-mode t)
(mouse-wheel-mode t)
(global-set-key [mouse-4] '(lambda () (interactive) (scroll-down 3)))
(global-set-key [mouse-5] '(lambda () (interactive) (scroll-up   3)))

TeraTerm の設定

設定(Setup)>その他の設定(Additional settings)>制御シーケンス(Control Sequence)にあります。

  • □ マウスイベント通知要求を受けいれる
  •  □ Controlキーを押している間はマウスイベントを通知しない

あたりがその設定。デフォルトONのはず。

操作性のメモ

mouse-4, mouse-5 はマウスホイールに割り当てられているので、ぐりぐりすればスクロール出来る。

いつもは出来ないマウスを使ったカーソルの移動も出来るようになる。 ちゃんと、クリックした位置へ Emacs のカーソルが移動します。

但し、クリックが Emacs へ渡されるので、これまで出来ていた、TeraTerm 上で右クリックして、 Windowsクリップボードの内容を貼り付ける機能は使えなくなってしまった。

でも大丈夫。それは Ctrl+右クリック でできます。

マウストラッキングモードでの TeraTerm の操作

  • Ctrl+左ドラッグ

    • コピー
  • Ctrl+右クリック

    • ペースト
  • Ctrl+Alt+左ドラッグ

    • 矩形コピー

マウストラッキングモードでの Emacs の操作

  • 左クリック

    • カーソルがその位置に移動しマークをセット
  • 右クリック

    • カーソルが移動しマークからその位置までを選択
  • 左ドラッグ

    • ドラッグ開始位置にマークをセットし、キーアップ位置までを選択
  • 左ドラッグ

    • 右クリックと同じ。キーアップしたところがクリック位置とみなされる。
  • ホイールを上へ

    • スクロールダウン
  • ホイールを下へ

    • スクロールアップ

終わり

Emacs の標準のインデント動作を変更したい -- namespace ではインデントしたくないんだ! --

問題点

namespace の {} 内でインデントしてしまう。

namespace My {
    namespace World {
        void func()
        {
            // ...
        }
    }
}

namespace はネストすることも多いし、インデントしたくないなあ……。 例えばこんな感じで…。

namespace My {
namespace World {

void func()
{
    // ...
}

} // End of namespace World
} // End of namespace My

さらにもともとインデントしているファイルは 勝手にインデントを変更せず、そのままにしたいなあ…

namespace Their {
    namespace World {
        void func()
        {
            // ...
        }
    }
}

処方箋

C-c C-s (c-show-syntactic-information) を実行すると、カーソルがある位置のインデント情報が表示される。 たとえば

namespace My {
| <== ここにカーソル
}

ここにカーソルを置いてC-c C-s を実行すると、

Syntactic analysis: ((innamespace 52) (topmost-intro 52))

などと表示される。innamespace というのを覚えておく。

その後 C-c C-o (c-set-offset) で innamespace のインデント設定を変更する。

C-c C-o
Syntactic symbol to change: innamespace    # innamespace と入力
innamespace offset (default +): 0          # 0 と入力

などとする。これで namespace 内で自動的にインデントしなくなる。(すでにインデントされている行でTABを打つなどするとインデントがずれるので、元のコードをそのままにしたいときは注意が必要)

これを init.el に書いておけばOK。下記のように。

(defunc my-c-mode-common-hook ()
    ;; styleの選択
    (c-set-style "stroustrup")

    ;; インデント規則 (c-set-style の後に設定する必要がある) 
    (c-set-offset 'innamespace 0)
)
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)

注意点としては、 c-set-offset の指定は c-set-style の後にやらないといけないこと。先に書くと、c-set-style の設定で上書きされて消えてしまう。

旧処方箋(2020年現在、これだと動かなかった c-set-style との順番のせいかもしれない)

Emacs の設定を変更。c-offset-alist の innamespace などの設定を (c-lineup-dont-change) に変える。

Customize で C Offsets Alistを変える。

  1. M-x customize
    • Programming で Enter
    • Language で Enter
    • C を選択して Enter
    • C Offsets Alist を選択して Enter
    • (M-x customize-apropos で c offsets alist を検索してもいける)
  2. C Offsets Alist の 一覧が開く
  3. 下記の設定値の左のチェックボックスを ON にして…
    • namespace-open
    • namespace-close
    • innamespace
  4. 値を (c-lineup-dont-change) に書き換える
  5. State ボタンをおして…
  6. Save for Future Sessions を実行 ==> .emacs とかに設定が書かれる。

以上。

おまけ

  • extern "C" {} とかもインデントしたくない場合は
    • extern-lang-open
    • extern-lang-close
    • inextern-lang
  • も同じ設定にする。

参考資料