clang-formatで*.generated.hを勝手にソートさせない方法
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 UE4UE4はリフレクションに関するデータを
*.generated.h
として生成します。
また、このファイルは型を宣言するヘッダーファイルの中で、1番最後にincludeする必要があります。
その他の参考資料: Unreal Property System (Reflection)
.clang-formatの設定方法
これを.clang-format
へ追加する
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '.*\.generated\.h'
Priority: 100
- Regex: '.*'
Priority: 1
おわり
簡単な解説
設定紹介なので、この時点で完全にオチがついてるのですが、このまま終わってしまうのは少し味気ないので、簡単な解説を書こうと思います。
Clang formatには色々な設定項目があり、#include
のソートなどに関するIncludeBlocks
という項目があります。
こんな感じで設定を追加します。
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
という項目を追加で設定することで、正規表現を使ったグループ分けが可能になります。
例えば、次の様に設定をすると
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
と他のヘッダーファイルは区別したほうがわかりやすいと思ったためです。
参考資料
- .clang-format for UE4 https://gist.github.com/intinig/9bba3a3faee80250b781bf916a4ab8b7
- Clang-Format Style Options — Clang 15.0.0git documentation https://clang.llvm.org/docs/ClangFormatStyleOptions.html
Discussion