🐡

clang-formatで*.generated.hを勝手にソートさせない方法

2022/03/01に公開

Linter、いいですよね。フォーマットに関する不毛な議論を全て請け負ってくれます。
ですが、たまにプロジェクトでのルールに反したフォーマットをすることがあります。例えば、UnrealEngineのソースコードで*.generated.hを1番後ろ以外にソートしてしまうとか。

今までは*.generated.hの前にコメントを入れてソートされない様にしていたのですが、.clang-formatの設定項目を眺めていたらこの問題に適した設定項目があったのでご紹介します。

*.generated.hとは

*.generated.hは、UnrealEngineが生成する特別なヘッダーファイルで、UnrealEngineが提供するGarbage collectorなどの素晴らしいシステムとC++の橋渡しになる闇のファイルです。
このヘッダーファイルの詳細な動作は知りませんが、諸々の事情により1番最後にincludeする必要があります。

UE4 will generate all the reflection data and put it into this file.
You must include this file as the last include in the header file that declares your type.
Unreal Reflection System - Introduction to C++ Programming in UE4

UE4はリフレクションに関するデータを*.generated.hとして生成します。
また、このファイルは型を宣言するヘッダーファイルの中で、1番最後にincludeする必要があります。

その他の参考資料: Unreal Property System (Reflection)

.clang-formatの設定方法

これを.clang-formatへ追加する

.clang-format
IncludeBlocks: Regroup
IncludeCategories:
  - Regex: '.*\.generated\.h'
    Priority: 100
  - Regex: '.*'
    Priority: 1

おわり

簡単な解説

設定紹介なので、この時点で完全にオチがついてるのですが、このまま終わってしまうのは少し味気ないので、簡単な解説を書こうと思います。

Clang formatには色々な設定項目があり、#includeのソートなどに関するIncludeBlocksという項目があります。

こんな感じで設定を追加します。

.clang-format
  BasedOnStyle: Google
+ IncludeBlocks: Merge

例えば次のようなヘッダーがあったとき

#include "b.h"

#include <lib/main.h>
#include "a.h"

IncludeBlocks: Mergeと設定すると、次のようにヘッダーがソートされます。
#includeを改行で区切っていても無視をするという設定です。

#include "a.h"
#include "b.h"
#include <lib/main.h>

他にも、IncludeBlocks: Preserveと設定すると、次の様になります。
改行区切りを尊重してくれる設定です。

#include "b.h"

#include "a.h"
#include <lib/main.h>

最後に、IncludeBlocks: Regroupというのがあり、今回採用した設定です。
IncludeCategoriesという項目を追加で設定することで、正規表現を使ったグループ分けが可能になります。

例えば、次の様に設定をすると

.clang-format
  BasedOnStyle: Google
+ IncludeBlocks: Regroup
+ IncludeCategories:
+   - Regex: '".*"'
+     Priority: 1
+   - Regex: '<.*>'
+     Priority: 2

次の様になります。
Priorityごとに改行で分けられ、その中でまたソートされます。

#include "a.h"
#include "b.h"

#include <lib/main.h>

他にも、SortPriorityという項目を追加すれば、同じPriorityの中でのソート順をいじれたり、CaseSensitiveという項目を追加すると、大文字小文字を区別して正規表現を適応させることができるようです。
ちなみに、複数の正規表現にマッチする場合、上の方にある設定が優先されるようです。
詳しくは公式ドキュメントをご覧ください。

冒頭の設定では、SortPriorityではなくPriorityを使って設定していますが、*.generated.hと他のヘッダーファイルは区別したほうがわかりやすいと思ったためです。

参考資料

Discussion