Uncrustifyのセッティング (4) アラインメント編 前編

はじめに
--------
次の設定の解説は空白設定にしようと思っていたが、項目が150項目もある。もはや、ちょちょっと解説するには苦痛なレベルだ。なので、空白設定はとりあえず飛ばそうと思う。

で、今回の設定の解説は、アラインメント設定についてだある。インデントとほとんど同じだけど、まぁもう少し細かな、行送りの時にはどうするとか、operatorキーワードに対してはどう位置揃えするとか、そういうことまで設定させてしまう。

あまりにも細かすぎて、ちょっとびっくりすると思うが、前回のインデントの設定も相当に細かいので、徐々に細かさに慣れてくる。しかし、uncrustifyというツールはパワフルだけど、誰も詳しく設定項目を解説してないのは何でだろうと思う。どうやら、UncrustifyXというツールが説明とかも付いているし、インタラクティブにUncrustifyの設定もできる便利ツールらしい。まだ試してはいないが、いつか試してみよう。

また、このアラインメントに関しては、「ラインスパン (Span)」、「カラム閾値 (Threshold)」、「カラムギャップ (Gap)」という3つのパラメータが登場する。Uncrustifyのドキュメントには、それぞれ以下のように説明されている。

- ラインスパン <br />
  ラインスパンというのは、アラインメントのための基準である。ラインスパンに行数を設定することで、前後何行分を見て位置揃えするかを判断する。これは、空行が間にあったり、カラムが非常に長いトークンが間にあったりする場合に、インテリジェントに位置揃えをしてくれる。単に、スパンとも呼ばれる。
- カラム閾値 <br />
  カラム閾値というのは、アラインメントのための基準である。カラム閾値に桁数を設定することで、位置揃えされるトークンの左側にあるトークンのカラム差に対する制限幅である。単に、閾値とも呼ばれる。
- カラムギャップ <br />
  カラムギャップというのは、アラインメントのための基準である。位置揃えされたトークンの左側に付ける半角空白数である。単に、ギャップとも呼ばれる。

だいたい、それぞれの概念を例示してみる。

```cpp:span_thresh_gap.cpp
// 4つの変数宣言の位置合わせについて考える。1つだけ、非常に変数型が長い。
{
   double d;
   int i;
   unsign long long ull;
   char c;
}

// ラインスパンが1行の場合、直前の行だけを見て位置揃えをする。
{
    double d;
    int    i;
    unsigned long long ull;
    char c;
}

// ラインスパンが2行の場合、前2行分を見て位置揃えをする。
{
    double d;
    int    i;
    unsigned long long ull;
    char   c;
}

// カラム閾値が2桁の場合、どの変数型(トークン)もカラム差が3文字以上なので、全く揃わない。
// (ラインスパンは1行)
{
    double d;
    int i;
    unsigned long long ull;
    char c;
}

// カラム閾値が3桁の場合、unsigned long long 以外のトークンのカラム差は3文字以内なので揃う。
// (ラインスパンは2行: そうでないと、int と char は行が離れていて、位置揃えの関係にならない。)
{
    double d;
    int    i;
    unsigned long long ull;
    char   c;
}

// カラムギャップは非常に簡単だ。
// ギャップを4桁に設定すれば、トークン (ここでは変数名) は少なくとも半角空白4個分のスペースを作る。
{
    double    d;
    int       i;
    char      c;
}
```

ただし、実際にUncrustifyのフォーマッタがこれらのアラインメントを実行するアルゴリズムはもう少し複雑だ。ユーザの期待した通りに整形してくれない場合もあるだろう。アラインメントの仕様については、リポジトリのdocumentation/配下にある"align-thresholds.txt"を参照してほしい。

アラインメント設定
------------------
なお、アラインメントオプションは、左側(行頭)にある半角空白やタブ文字はアライメント対象ではなく、それ以外の半角空白やタブ文字に対するオプションである。

| 項目                        | 説明                                                                                                                                                             |
|:----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| align_keep_tabs             | インデントではないタブ文字を残すか { true, false }                                                                                                               |
| align_with_tabs             | 整列位置合わせにタブ文字を利用するか { true, false }                                                                                                             |
| align_on_tabstop            | 整列位置合わせで次のタブストップ位置にするか { true, false }                                                                                                     |
| align_number_left           | 数値を左揃えにするかどうか (align_struct_init_spanと関連) { true, false }                                                                                        |
| align_func_params           | プロトタイプ宣言と関数定義で、変数定義の位置を揃えるかどうか { true, false }                                                                                     |
| align_same_func_call_params | 同じ名前を持つシングルライン関数のパラメータの位置を揃えるかどうか (関数名はすでにそれぞれ位置合わせされていなければならない。) { true, false }                  |
| align_var_def_star_style    | 変数定義でのポインタの'*'の位置揃えの方法 { 0: 型の一部、1: 変数名の一部、2: ダングリング }                                                                      |
| align_var_def_amp_style     | 変数定義での参照の'&'位置揃えの方法 { 0: 型の一部、1: 変数名の一部、2: ダングリング }                                                                            |
| align_var_def_colon         | 構造体ビットフィールドの':'を位置揃えするかどうか { true, false }                                                                                                |
| align_var_def_attribute     | 変数名の後の属性を位置揃えするかどうか { true, false }                                                                                                           |
| align_var_def_inline        | struct/enum/unionのメンバ変数定義を位置揃えするかどうか { true, false }                                                                                          |
| align_var_def_span          | 変数定義の位置揃えに対するラインスパン (0: 揃えない) { number }                                                                                                  |
| align_var_def_thresh        | 変数定義の位置揃えのカラム閾値 (0: 制限無し) { number }                                                                                                          |
| align_var_def_gap           | 変数定義の位置揃えのカラムギャップ { number }                                                                                                                    |
| align_assign_span           | 代入式の'='の位置揃えをするラインスパン (0: 揃えない) { number }                                                                                                 |
| align_assign_thresh         | 代入式の'='の位置揃えをするカラム閾値 (0: 制限無し) { number }                                                                                                   |
| align_enum_equ_span         | enumの'='の位置揃えをするラインスパン (0: 揃えない) { number }                                                                                                   |
| align_enum_equ_thresh       | enumの'='の位置揃えをするカラム閾値 (0: 制限無し) { number }                                                                                                     |
| align_var_struct_span       | struct/unionのメンバー定義の位置揃えをするラインスパン (0: 揃えない) { number }                                                                                  |
| align_var_struct_thresh     | struct/unionのメンバー定義の位置揃えをするカラム閾値 (0: 制限無し) { number }                                                                                    |
| align_var_struct_gap        | struct/unionのメンバー定義のカラムギャップ { number }                                                                                                            |
| align_struct_init_span      | structのメンバ変数の指定初期化の位置揃えのラインスパン (0: 揃えない) { number }                                                                                  |
| align_typedef_gap           | typedefの型とシノニムの間の空白の最小数 { number }                                                                                                               |
| align_typedef_span          | 一行typedefの位置揃えをするラインスパン (0: 揃えない) { number }                                                                                                 |
| align_typedef_func          | typedefされた関数とその他typedefの位置揃えの方法 { 0: ミックスして位置を揃えない、1: 開丸括弧と型の位置を揃える、2: 関数の型名とその他の型名の位置を揃える }     |
| align_typedef_star_style    | typedefの'*'の位置揃えの方法 { 0: typedef typeで揃える、1: '*'を型名の一部とする、2: '*'を型名の一部としてダングリングする }                                     |
| align_typedef_amp_style     | typedefの'&'の位置揃えの方法 { 0: typedef typeで揃える、1: '&'を型名の一部とする、2: '&'を型名の一部としてダングリングする }                                     |
| align_right_cmt_span        | 行末コメントを位置揃えするラインスパン (0: 揃えない) { number }                                                                                                  |
| align_right_cmt_mix         | If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment { true, false }                                          |
| align_right_cmt_gap         | 行末コメントがこの桁数以上コードと離れている場合、位置揃えする (0: 揃えない) { number }                                                                          |
| align_right_cmt_at_col      | 行末コメントの位置揃えするカラム位置 (嬉しい副産物 'pulls in' コメント) (0: 揃えない) { number }                                                                 |
| align_func_proto_span       | プロトタイプ宣言の位置揃えのラインスパン (0: 揃えない) { number }                                                                                                |
| align_func_proto_gap        | プロトタイプ宣言の返値型と関数名の間のギャップカラム { number }                                                                                                  |
| align_on_operator           | 'operator'キーワードでプロトタイプ宣言を位置揃えするかどうか { number }                                                                                          |
| align_mix_var_proto         | プロトタイプと変数宣言のミックス整形するかどうか (trueの場合、align_var_def_XXXオプションがalign_func_proto_XXXオプションの代わりに使用される。) { true, false } |
| align_single_line_func      | 一行関数を関数プロトタイプと同様に位置揃えするかどうか (align_func_proto_spanを使用) { true, false }                                                             |
| align_single_line_brace     | 一行関数のオープンブレースを位置揃えするかどうか (align_single_line_func=trueの場合のみ) (align_func_proto_spanを使用) { true, false }                           |
| align_single_line_brace_gap | align_single_line_braceに対するギャップ { number }                                                                                                               |
| align_nl_cont               | バックスラッシュ-改行で囲われた複数行マクロを位置揃えするかどうか (ただし、マクロが複数行コメントを内包している場合、うまく動かない) { true, false }             |
| align_pp_define_span        | プリプロセッサ定義のボディの位置揃えのスパン (0: 揃えない) { number }                                                                                            |
| align_pp_define_gap         | プリプロセッサ定義のラベルと値の間の空白の最小幅 { number }                                                                                                      |
| align_pp_define_together    | マクロ関数とマクロ変数を一緒に位置揃えするかどうか { true, false }                                                                                               |
| align_left_shift            | '<<'で開始する行を前の行の'<<'の位置に揃えるかどうか (デフォルト: true) { true, false }                                                                          |
| align_oc_msg_spec_span      | Objective-Cのメッセージ仕様宣言のスパン (0: 揃えない) { number }                                                                                                 |
| align_oc_msg_colon_span     | Objective-Cのメッセージコールにおけるパラメータ':'の位置揃えのスパン (0: 揃えない) { number }                                                                    |
| align_oc_msg_colon_first    | trueの場合、最初のパラメータの位置に後続パラメータの位置を揃える (メッセージが短くとも) { true, false }                                                          |
| align_oc_decl_colon         | Objective-Cの'+'もしくは'-'宣言行のパラメータを':'で位置揃えするかどうか { true, false }                                                                         |

僕の設定
--------
```bash:align.cfg
align_keep_tabs                          = false
align_with_tabs                          = false
align_on_tabstop                         = false
align_number_left                        = false
align_func_params                        = true
align_same_func_call_params              = false
align_var_def_star_style                 = 0
align_var_def_amp_style                  = 0
align_var_def_colon                      = true
align_var_def_attribute                  = true
align_var_def_inline                     = true
align_var_def_span                       = 2
align_var_def_thresh                     = 6
align_var_def_gap                        = 0
align_assign_span                        = 2
align_assign_thresh                      = 6
align_enum_equ_span                      = 2
align_enum_equ_thresh                    = 6
align_var_struct_span                    = 2
align_var_struct_thresh                  = 6
align_var_struct_gap                     = 0
align_struct_init_span                   = 1
align_typedef_gap                        = 0
align_typedef_span                       = 2
align_typedef_func                       = 1
align_typedef_star_style                 = 0
align_typedef_amp_style                  = 0
align_right_cmt_span                     = 4
align_right_cmt_mix                      = false
align_right_cmt_gap                      = 1
align_right_cmt_at_col                   = 0
align_func_proto_span                    = 2
align_func_proto_gap                     = 0
align_on_operator                        = true
align_mix_var_proto                      = false
align_single_line_func                   = true
align_single_line_brace                  = true
align_single_line_brace_gap              = 0
align_nl_cont                            = true
align_pp_define_span                     = 2
align_pp_define_gap                      = 0
align_pp_define_together                 = true
align_left_shift                         = true
align_oc_msg_spec_span                   = 0
align_oc_msg_colon_span                  = 0
align_oc_msg_colon_first                 = false
align_oc_decl_colon                      = false
```

ALIGN_KEEP_TABS
---------------
```cpp:align_keep_tabs.cpp
// 整形前
int var,--->var2;

// align_keep_tabs = true
// タブ文字は残る。
int var,--->var2;

// align_keep_tabs = false
// タブ文字は半角空白4個に展開される。
// この半角空白の数は、input_tab_sizeとoutput_tab_sizeに依存する。
int var,....var2;
```

ALIGN_WITH_TABS
---------------
```cpp:align_with_tabs.cpp
// 整形前
int foo = 1;
int bar = 1;
int foobar = 2;
int foobarfoobar = 4;

// align_with_tabs = true
// '='の位置合わせでは、このようにタブ文字が使われる。(output_tab_size = 4)
int foo>--->---> = 1;
int bar>--->---> = 1;
int foobar->---> = 2;
int foobarfoobar = 4;

// align_with_tabs = false
// 半角空白のみで位置が揃えられる。
int foo......... = 1;
int bar......... = 1;
int foobar...... = 2;
int foobarfoobar = 4;
```

ALIGN_ON_TABSTOP
----------------
```cpp:align_on_tabstop.cpp
// 整形前
int foo = 1;
int bar = 1;
int foobar = 2;
int foobarfoobar = 4;

// align_on_tabstop = true
// '='の位置合わせでは、次のタブ位置で揃えられる。(output_tab_size = 4)
int foo             = 1;
int bar             = 1;
int foobar          = 2;
int foobarfoobar    = 4;

// align_on_tabstop = false
// 一番短く揃えられる位置で揃えられる。
int foo          = 1;
int bar          = 1;
int foobar       = 2;
int foobarfoobar = 4;
```

ALIGN_NUMBER_LEFT
-----------------
```cpp:align_number_left
// align_number_left = true (&& align_struct_init_span = 10)
// 構造体フィールドの初期化の数値リテラルが右寄せになる。
struct Foo foo = {
    .x =   1,
    .y =  23,
    .z = 456,
};
```

ALIGN_FUNC_PARAMS
-----------------
```cpp:align_func_params.cpp
// align_func_params = true
// 関数定義やプロトタイプ宣言の引数リストの型と変数名の間が位置揃えされる。
void func(
    int   param1,
    short param2,
    long  param3);

// align_func_params = false
// 関数定義やプロトタイプ宣言の引数リストの型と変数名の間は揃えられない。(何しない)
void func(
    int param1,
    short param2,
    long param3);
```

ALIGN_SAME_FUNC_CALL_PARAMS
---------------------------
```cpp:align_same_func_call_params
// align_same_func_call_params = true
// 同じ関数呼び出すが複数行並んでいた場合、引数の位置を揃える。
void function(void)
{
    foo(abc,  def,  xyz);
    foo(abcd, efgh, ijkl);
}

// align_same_func_call_params = false
// 同じ関数呼び出しが並んでいても、引数の位置は自動的には揃わない。(何もしない)
void function(void)
{
    foo(abc, def, xyz);
    foo(abcd, efgh, ijkl);
}
```

ALIGN_VAR_DEF_STAR_STYLE
------------------------
```cpp:align_var_def_star_style.cpp
// align_var_def_star_style = 0
// 変数宣言におけるポインタの'*'を変数の型に付ける。
    int     integer;
    int*    i_ptr;
    char*   c_ptr;
    double* d_ptr;
    float*  f_ptr;

// align_var_def_star_style = 1
// 変数宣言におけるポインタの'*'を変数名の先頭に付け、'*'は非ポインタ型の変数名の先頭位置と位置揃えする。
    int    integer;
    int    *i_ptr;
    char   *c_ptr;
    double *d_ptr;
    float  *f_ptr;

// align_var_def_star_style = 2
// 変数宣言におけるポインタの'*'を変数名の先頭に付ける。変数名の先頭で位置揃えを行う。'*'はダングリングさせる。
    int     integer;
    int    *i_ptr;
    char   *c_ptr;
    double *d_ptr;
    float  *f_ptr;
```

ALIGN_VAR_DEF_AMP_STYLE
------------------------
```cpp:align_var_def_amp_style.cpp
// align_var_def_amp_style = 0
// 変数宣言における参照の'&'を変数の型に付ける。
    int     integer;
    int&    i_ptr;
    char&   c_ptr;
    double& d_ptr;
    float&  f_ptr;

// align_var_def_amp_style = 1
// 変数宣言における参照の'&'を変数名の先頭に付け、'&'は非参照型の変数名の先頭位置と位置揃えする。
    int    integer;
    int    &i_ptr;
    char   &c_ptr;
    double &d_ptr;
    float  &f_ptr;

// align_var_def_amp_style = 2
// 変数宣言における参照の'&'を変数名の先頭に付ける。変数名の先頭で位置揃えを行う。'&'はダングリングさせる。
    int     integer;
    int    &i_ptr;
    char   &c_ptr;
    double &d_ptr;
    float  &f_ptr;
```

ALIGN_VAR_DEF_COLON
-------------------
```cpp:align_var_def_colon.cpp
// align_var_def_colon = true
// 構造体のビットフィールドの':'の位置を揃える。
struct Bitfields
{
    unsigned int bit1     : 1;
    unsigned int bit2_8   : 7;
    unsigned int bit9_16  : 8;
    unsigned int bit17_32 : 16;
};

// align_var_def_colon = false
// ビットフィールドを揃えない。(何もしない)
struct Bitfields
{
    unsigned int bit1 : 1;
    unsigned int bit2_8 : 7;
    unsigned int bit9_16 : 8;
    unsigned int bit17_32 : 16;
};
```

ALIGN_VAR_DEF_ATTRIBUTE
-----------------------
```cpp:align_var_def_attribute.cpp
// align_var_def_attribute = true
// 変数属性の'__attribute__'の位置を合わせる。
unsigned char original[128] __attribute__(aligned(128));
unsigned char copy[128]     __attribute__(aligned(128));
unsigned char result[128]   __attribute__(aligned(128));

// align_var_def_attribute = false
// 変数属性の'__attribute__'の位置を合わせない。(何もしない)
unsigned char original[128] __attribute__(aligned(128));
unsigned char copy[128] __attribute__(aligned(128));
unsigned char result[128] __attribute__(aligned(128));
```

ALIGN_VAR_DEF_INLINE
--------------------
```cpp:align_var_def_inline.cpp
// align_var_def_inline = true
// 構造体のメンバ変数の宣言(型名とメンバ変数名)の位置を揃える。
struct Foo
{
    int   bar1;
    short bar2;
}

// align_var_def_inline = false
// 構造体のメンバ変数の宣言(型名とメンバ変数名)の位置を揃えない。(何もしない)
struct Foo
{
    int bar1;
    short bar2;
}
```

ALIGN_VAR_DEF_SPAN
------------------
```cpp:align_var_def_span.cpp
// align_var_def_span = 0
// 変数定義の位置揃えは行わない。
{
    int a;
    char bbb;
    long ccc;

    float dddd;
    double eeeeee;


    unsigned int ff;
    unsigned char gggg;
}

// align_var_def_span = 1;
// 行が連続した変数定義の範囲だけ一緒に位置揃えを行う。
{
    int  a;                 // aligned 1
    char bbb;               // aligned 1
    long ccc;               // aligned 1

    float  dddd;            // aligned 2
    double eeeeee;          // aligned 2


    unsigned int  ff;       // aligned 3
    unsigned char gggg;     // aligned 3
}

// align_var_def_span = 2;
// 空行が1行開いている変数定義でも一緒に位置揃えを行う。
{
    int    a;               // aligned 1
    char   bbb;             // aligned 1
    long   ccc;             // aligned 1

    float  dddd;            // aligned 1
    double eeeeee;          // aligned 1


    unsigned int  ff;       // aligned 2
    unsigned char gggg;     // aligned 2
}

// align_var_def_span = 3   ;
// 空行が2行開いている変数定義でも一緒に位置揃えを行う。
{
    int           a;        // aligned with all
    char          bbb;      // aligned with all
    long          ccc;      // aligned with all

    float         dddd;     // aligned with all
    double        eeeeee;   // aligned with all


    unsigned int  ff;       // aligned with all
    unsigned char gggg;     // aligned with all
}
```

ALIGN_VAR_DEF_THRESH
--------------------
```cpp:align_var_def_thresh.cpp
// align_var_def_thresh = 3
// double(6文字)とint(3文字)のカラム差(3文字)は位置揃えされるが、
// long(4文字)とunsigned char(13文字)のカラム差(9文字)は位置揃えされない。
{
    float  dddd;
    double eeeeee;
    int    a;
    char   bbb;
    long   ccc;
    unsigned int  ff;
    unsigned char gggg;
}

// align_var_def_thresh = 2
// floatとdouble、intとcharとlong、unsigned intとunsigned charは揃えられるが、
// doubleとint、longとunsigned charは位置揃えされない。
{
    float  dddd;
    double eeeeee;
    int  a;
    char bbb;
    long ccc;
    unsigned int  ff;
    unsigned char gggg;
}
```

ALIGN_VAR_DEF_GAP
-----------------
```cpp:align_var_def_gap.cpp
// align_var_def_gap = 0 (align_var_def_gap = 1も同じ)
// 変数の型と変数名の間に半角空白1文字分の余白を空ける。
{
    float         dddd;
    double        eeeeee;
    int           a;
    char          bbb;
    long          ccc;
    unsigned int  ff;
    unsigned char gggg;
}

// align_var_def_gap = 4
// 変数の型と変数名の間を最小で半角空白4文字分を確保して位置揃えする。
{
    float        ....dddd;
    double       ....eeeeee;
    int          ....a;
    char         ....bbb;
    long         ....ccc;
    unsigned int ....ff;
    unsigned char....gggg;
}
```