読者です 読者をやめる 読者になる 読者になる

Eclipse CDT から clang-format を使って書式整形をしよう

C++ Eclipse Clang プログラミング Mac Linux

そろそろ物理屋さんも vim とか Emacs でソフト開発するのやめませんか? Eclipse とかにしませんか? ということで、ソフトをバリバリ書いてる人は Eclipse CDT (宇宙・素粒子系の物理屋Java 使わない) をご使用中のことと思います。

LLVM/Clang実践活用ハンドブック

LLVM/Clang実践活用ハンドブック

Eclipse4.4 完全攻略 (完全攻略シリーズ)

Eclipse4.4 完全攻略 (完全攻略シリーズ)

さて、Eclipse CDT など IDE の便利な機能にコードの自動整形 (formatter) があります。自分も最近は複数人でソフトの共同開発をすることが多くなってきたので、Google の coding style で統一しようという話になったのですが、意図的に特殊な整形を自分でしていた箇所まで整形し直してくれちゃうものだから、これは不便でしょうがない。

例えば、以下のコードは意図的にスペースを多用しています。

CI[ 5] = D[13] ^ D[12] ^ D[11] ^ D[ 9] ^ D[ 8] ^ D[ 5] ^ D[ 4] ^ D[ 0] ^
         C[ 0] ^ C[ 4] ^ C[ 5] ^ C[ 8] ^ C[ 9] ^ C[11] ^ C[12] ^ C[13];
CI[ 6] = D[14] ^ D[13] ^ D[12] ^ D[10] ^ D[ 9] ^ D[ 6] ^ D[ 5] ^ D[ 1] ^
         C[ 1] ^ C[ 5] ^ C[ 6] ^ C[ 9] ^ C[10] ^ C[12] ^ C[13] ^ C[14];
if (asic == 0) {
  WriteRegisterPartially(0x4D, channel,      channel,      enable ? 0x1 : 0x0);
} else if (asic == 1) {
  WriteRegisterPartially(0x4D, channel + 16, channel + 16, enable ? 0x1 : 0x0);
} else if (asic == 2) {
  WriteRegisterPartially(0x4E, channel,      channel,      enable ? 0x1 : 0x0);
} else if (asic == 3) {
  WriteRegisterPartially(0x4E, channel + 16, channel + 16, enable ? 0x1 : 0x0);
}

これに Eclipse CDT で自動整形をかけるとどうなるかというと、Google スタイルの場合はそれぞれ次のようになります。読めやしませんね。

CI[5] = D[13] ^ D[12] ^ D[11] ^ D[9] ^ D[8] ^ D[5] ^ D[4] ^ D[0] ^ C[0]
    ^ C[4] ^ C[5] ^ C[8] ^ C[9] ^ C[11] ^ C[12] ^ C[13];
CI[6] = D[14] ^ D[13] ^ D[12] ^ D[10] ^ D[9] ^ D[6] ^ D[5] ^ D[1] ^ C[1]
    ^ C[5] ^ C[6] ^ C[9] ^ C[10] ^ C[12] ^ C[13] ^ C[14];
if (asic == 0) {
  WriteRegisterPartially(0x4D, channel, channel, enable ? 0x1 : 0x0);
} else if (asic == 1) {
  WriteRegisterPartially(0x4D, channel + 16, channel + 16,
      enable ? 0x1 : 0x0);
} else if (asic == 2) {
  WriteRegisterPartially(0x4E, channel, channel, enable ? 0x1 : 0x0);
} else if (asic == 3) {
  WriteRegisterPartially(0x4E, channel + 16, channel + 16,
      enable ? 0x1 : 0x0);
}

実は EclipseJava を書く場合には次の記法でコードを「保護」することができます。off/on で囲まれた箇所は、自動整形が無視されます。

// @formatter:off
System.out.println("Hello World!");
// @formatter:on

しかし Eclipse CDT ではこの機能が実装されていないため (Java に比べて色々と貧弱)、Eclipse の標準機能だけでは C++ のコードに自動整形をかけると悲しいことになります。さて、じゃあどうやって自分の手動整形を保護するかというと、外部の formatter と Eclipse plugin を使って解決します。

clang-format を入れる

clang-format とは、その名の通り Clang に付属する formatter です。コマンドラインから動作し、C/C++/ObjC のファイルを整形することができます。ただし、OS XXcode を入れても付属してきません。以下の方法で自分で build します。ここでは、ver 3.7 以降 (近日公開される予定) を入れます (ここらへん参照)。バイナリの配布されている 3.5 では、以下の説明の機能は動作しません。

$ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd llvm/tools
$ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
$ cd ../..
$ cd llvm/projects
$ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
$ cd ../..
$ ./configure  --enable-optimized
$ make -j 8
$ sudo mv Release+Asserts/bin/clang-format /usr/local/bin/

これで、/usr/local/bin に clang-format が入ります。他のものは一切必要ありません。OS X に入っている標準の Clang が 3.5 でも、clang-format は単体で動作するので、Clang 自体を汚す危険もありません。

CppStyle を入れる

次に、clang-format を Eclipse で使うための plugin を導入します。https://github.com/wangzw/cppstyle の説明の通りですが、以下のようにします。

$ sudo curl -L "http://google-styleguide.googlecode.com/svn/trunk/cpplint/cpplint.py" -o /usr/local/bin/cpplint.py
$ sudo chmod a+x /usr/bin/cpplint.py

次に http://wangzw.github.io/CppStyle/updateEclipse の Help → Install New Software で追加したあと Eclipse を再起動し、Preference → C/C++ → Code Style → Formatter から Code Formatter を CppStyle (clang-format) にします。そのままにしておくと、Google スタイルが適用されます。

さて、clang-format では手動で整形した箇所を保護する機能があります。以下のようなコメントを追加することで、off/on の間に挟まれた領域は,clang-format の整形が適用されません。めでたしめでたし。繰り返しますが、Clang 3.5 の clang-format では動作しません。

if (asic == 0) {  // clang-format off
  WriteRegisterPartially(0x4D, channel,      channel,      enable ? 0x1 : 0x0);
} else if (asic == 1) {
  WriteRegisterPartially(0x4D, channel + 16, channel + 16, enable ? 0x1 : 0x0);
} else if (asic == 2) {
  WriteRegisterPartially(0x4E, channel,      channel,      enable ? 0x1 : 0x0);
} else if (asic == 3) {
  WriteRegisterPartially(0x4E, channel + 16, channel + 16, enable ? 0x1 : 0x0);
} else { // clang-format on