測試clang-format的格式化效果

  我本身寫的業餘框架已告一段落,主體功能已完成,剩下的就是優化。第一個要優化的,就是代碼格式。我一直是用編輯器寫代碼的,從以前的UltraEdit到notepad++到sublime text,再到如今的VS Code。因爲代碼都是我一我的寫,風格也比較統一,雖說不上美觀,但至少說得過去。但尋思着之後萬一有人要用這代碼,總得有個較爲通用的代碼風格才行,並且我也不太可能去人工約束別人怎麼寫,那就用工具吧。html

  C++不像Java、C#、TypeScript這些語言,他們都有較爲通用的代碼風格標準,比較通用的IDE,基本是自帶代碼格式化,所以總體上來講比較容易統一。但C++就沒有,好比我在公司是用Visual Studio,在家有時候用的VS Code,有時候用的Qt。如今流行的C++代碼格式化工具,大概有3個:clang-format、uncrustify、astyle。linux

  clang-format是隨LLVM項目而來的後起之秀,也是此次測試的重點。緣由是它的開發如今是最活躍的,格式化選項是最多的,集成也是最多的(VS2017之後有集成,VS Code有插件,Qt在新版本中已經集成)。我花了點時間,嘗試瞭解clang-format的配置,並作了些測試。git

/* 測試clang-format格式化效果
 */

class Test
{
// 對齊這個public修飾符 AccessModifierOffset: -2
    public:
};

// 括號斷行後參數對齊方式 AlignAfterOpenBracket
void ttttttt(int aaaaaaa, int bbbbbbbbb,int ccccccccc,int ddddddddd, int eeeeeeeeeeeeeee, int ffffffffffffffffffffffffffffffff)
{
}
// TODO: 不能實現下面的對齊方式
// 1. 下一行長度大於上一行
// 2. 換行縮進爲4格
//
void ttttttt(int aaaaaaa, int bbbbbbbbb,int ccccccccc,
    int ddddddddd, int eeeeeeeeeeeeeee, int ffffffffffffffffffffffffffffffff)
{
}

// 賦值時等號對齊 AlignConsecutiveAssignments
int aaaaaaa = 222222222;
int     b = 7;
int  ccccc = 99999;

// 變量名左對齊,應該和上面的衝突 AlignConsecutiveDeclarations
int         aaaa = 12;
float        b = 23;
std::string   ccc = 23;

// 宏對齊 AlignConsecutiveMacros
#define SHORT_NAME       42
#define     LONGER_NAME         0x007f
#define  EVEN_LONGER_NAME      (2)
#define    foo(x)             (x * x)
#define  bar(y, z)         (y + z)

// 斷行符的對齊 AlignEscapedNewlines
#define A   \
        int aaaa; \
    int b;     \
    int dddddddddd

// 運算變量對齊 AlignOperands
int sum = aaaaaaaaaaa + bbbbbbbbbb
    + ccccccccccccccccccc + ddddddddddddddd + eeeeeeeeeee + fff;

// 是否對齊行尾註釋 AlignTrailingComments
int x; // test xxxxxxxx
int yyyyyyyyyyyyy; // test yyyyyyyyy
int zzzz; // test zzzzzzz

// 調用函數時,參數的斷行方式 AllowAllArgumentsOnNextLine
looooooooooooooooooooooooooooooooooooooooooooooong_call(aaaaaaaaaa,bbbbbbbb,cccccccc);

// 構造函數初始化列表斷行方式 AllowAllConstructorInitializersOnNextLine
class LongInitializers
{
public:
    LongInitializers(): aaaaaaaaaaaaaaaaa(1), bbbbbbbbbbbbbbbbbb(2), ccccccccccccc(3)
    {
    }
};

// 函數聲明時參數的斷行方式 AllowAllParametersOfDeclarationOnNextLine
// 上面的AlignAfterOpenBracket優先級更高,會影響這個
int ddddddddddddddddddddddddddddddddddddddddd(int aaaaaa, int bbbbbbb,int cccccccccccc);

// 是否把簡短的代碼合併成一行 AllowShortBlocksOnASingleLine
// 這個沒生效,並且和文檔也對不上了。文檔有好幾個值,配置只接受 true flase
if (a > b)
    a++;
while (true)
{
    a ++;
    b ++;
};

// switch的case是否合併成一行 AllowShortCaseLabelsOnASingleLine
switch(type)
{
    case 1 :
        a++;
        break;
}

// 簡短的函數能不能放一行 AllowShortFunctionsOnASingleLine
class TestFuncOneLine
{
    void test()
{
    a ++;
}
};

void not_one_line()
{
    a ++;
}

// if 語句能不能放一行 AllowShortIfStatementsOnASingleLine
if (a > b)
{
    a ++;
    if (0 == b)
    b ++;
}
else
{
    b++;
    if (0 == a)
    {
        a = 0;
        b = 0;
    }
}

// lambda表達式能不能放一行 AllowShortLambdasOnASingleLine
void lambda_one_line()
{
    auto lambda = [](int a, int b)
    {
    return a > b;
    };

    // TODO: 這裏人換行
    sort(a.begin(), a.end(), ()[] {
        return x < y;
    });
}


// for等循環能不能放一行 AllowShortLoopsOnASingleLine
// 這個測試沒生效
while (true)
{
    a ++;
};

do
{
    a ++;
}while(0);
do { a ++ } while(0);

// TODO:這個宏總被展開
#define TEST(a) do { a ++ } while(0)

// 函數聲明 返回類型以後要不要斷行 AlwaysBreakAfterDefinitionReturnType
int test() {} // 不換行
int
test() {} // 換行

// 函數實現 返回類型以後要不要斷行 AlwaysBreakAfterReturnType
int
test()
{
    a ++;
}


// 字符串換行時,等號後面要不要換行 AlwaysBreakBeforeMultilineStrings
aaaa =
    "bbbb" // 換行
    "cccc";

aaaa = "bbbb" // 不換行
    "cccc";

// 模板聲明是否換行 AlwaysBreakTemplateDeclarations
template <typename T>
T foo() {
}
template <typename T>
T foo(int aaaaaaaaaaaaaaaaaaaaa,
      int bbbbbbbbbbbbbbbbbbbbb) {
}

// 調用函數時,參數是否獨佔一行 BinPackArguments
func(
    a,
    b,
    c
);

funcccccccccccccccccccccccccc(aaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb,
    ccccccccccccccccc, cccccccccccccccccccc);

// 聲明或者實現函數時,參數是否獨佔一行 BinPackParameters
int
funcccccccccccccccccccccccccc(aaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb,
    ccccccccccccccccc, cccccccccccccccccccc);

// 控制大括號的斷行方式 BraceWrapping
// BreakBeforeBraces的值爲Custom纔有效
class foo {};

// 運算符(=、+、、*等)換行方式 BreakBeforeBinaryOperators
LooooooooooongType loooooooooooooooooooooongVariable = someLooooooooooooooooongFunction();

bool value = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +
                     aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==
                 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
             aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >
                 ccccccccccccccccccccccccccccccccccccccccc;

// 大括號換行方式 BreakBeforeBraces

// 雙目運算符的斷行方式 BreakBeforeTernaryOperators
int a = a > b ? a : b;
veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription ? firstValue : SecondValueVeryVeryVeryVeryLong;

// 初始化列表換行方式 BreakConstructorInitializers
// 繼承的換行方式 BreakInheritanceList
class Tttttttttttttttttttttttttttttttttttttttttttttttttttttest : Aaaaaa,Bbbbbbbbb
{
public:
    Tttttttttttttttttttttttttttttttttttttttttttttttttttttest() : Aaaaaa(),Bbbbbbbbb() {}
};

// 字符串是否容許換行
std::string str = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong str";

// 單行字符數 ColumnLimit

// 控制註釋中哪些內容不容許換行的正則 CommentPragmas

// 是否合併命名空間到一行 CompactNamespaces
namespace Foo { namespace Bar { // 合併
}}
// 不合並
namespace Foo {
namespace Bar {
}
}

// 初始化列表是否全放到一行 ConstructorInitializerAllOnOneLineOrOnePerLine
SomeClass::Constructor()
    : aaaaaaaa(aaaaaaaa), aaaaaaaa(aaaaaaaa), aaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa) {
  return 0;
}

// 初始化列表換行時,縮進的寬度 ConstructorInitializerIndentWidth
// 發生換行時,縮進的寬度 ContinuationIndentWidth

// 大括號數組是否有空格 Cpp11BracedListStyle
vector<int> x{1, 2, 3, 4}; // cpp11風格,沒有
vector<int> x{ 1, 2, 3, 4 }; //// 引用和指針的對齊方式 DerivePointerAlignment
// 不知道這個是用來幹啥的

// 徹底不格式化(爲啥會有這個選項,不格式化不調用不就好了麼) DisableFormat

// 自動根據當前文件其餘地方的格式來格式化函數參數 ExperimentalAutoDetectBinPacking
// 好比說其餘地方的參數是每一個參數佔一行,那它決定按每一個參數佔一行來格式化
// 這個是實驗性的

// 是否自動添加命名空間結束註釋 FixNamespaceComments
namespace a
{
foo();
} // 按理來講這裏會加個 namspace a的註釋,但測試發現沒生效。不過若是這裏有註釋,就會被修正

// 一些第三方的foreach循環宏定義,好比QT的 ForEachMacros

// include的合併方式 IncludeBlocks
// 通常按名字來排,按空行分組(由於有些順序是特定的),注意下面有個 SortIncludes 選項
#include "b.h"

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


// include 優先級 IncludeCategories
// 當上面的IncludeBlocks設置爲Regroup,會把include所有排序,排序規則就按這個來

// include規則 ,和上面的差很少 IncludeIsMainRegex

// switch的case縮進 IndentCaseLabels
switch(a)
{
case 1 : break; // 不縮進
    case 2 : break; // 縮進
}

// togo標籤是否縮進,文檔裏有,程序不認這個選項了 IndentGotoLabels

// 多層宏定義鑲嵌時,縮進方式 IndentPPDirectives
#if FOO
#if BAR
#include <foo>
#endif
#endif

// 縮進寬度 IndentWidth

// 當返回類型和函數名斷行時,函數名是否縮進 IndentWrappedFunctionNames
// 真有這麼長的類型和函數名嗎?
LoooooooooooooooooooooooooooooooooooooooongReturnType
    LoooooooooooooooooooooooooooooooongFunctionDeclaration();

// 代碼塊開始時,要不要空一行 KeepEmptyLinesAtTheStartOfBlocks
// 這個沒生效,多是須要大括號在行尾的Java風格纔有效
if (true) {

    test(); // 上面空一行
}

// 指定格式化提哪一個語言 Language

// 匹配一對開始和結束的宏 MacroBlockBegin MacroBlockEnd
NS_MAP_BEGIN
  foo();
NS_MAP_END

// 容許連續空多少行 MaxEmptyLinesToKeep
if (true)
{
    a ++;


    b ++;
}

// 命名空間裏的代碼是否縮進 NamespaceIndentation
namespace out {
int i; // 不縮進
namespace in {
  int i; // 縮進
}
}

// 表示命名空間的宏(我用不着,暫時不測試) NamespaceMacros

// 這幾個Penalty暫時不知道是啥,下面的連接有討論
// https://stackoverflow.com/questions/26635370/in-clang-format-what-do-the-penalties-do
// 大概意思是當恰好超過行寬,但又不超幾個字符時,若是斷行
// 我試了下PenaltyBreakBeforeFirstCallParameter = 19, PenaltyExcessCharacter = 10
// 時,下面那個函數調用就不換行。但這個數值是根據算法來的,算法把各類因素給定一些值,得
// 出不一樣換行時的penalty值,其大小沒有標準可參考

// PenaltyBreakAssignment
auto loooooooooooooooooooooooooooooooooooooooooooooooooooooooooosong_var = "abc";
// PenaltyBreakBeforeFirstCallParameter
Namespaces::Are::Pervasive::SomeReallyVerySuperDuperLooooooooongFunctionName(args);
// PenaltyBreakComment
// PenaltyBreakFirstLessLess
// PenaltyBreakString
// PenaltyBreakTemplateDeclaration
// PenaltyExcessCharacter
// PenaltyReturnTypeOnItsOwnLine

// 指針的*號放哪 PointerAlignment
int* a; // left
int *a; // right


// 匹配字符串中代碼的正則,若是匹配到,字符串的代碼將會被格式化 RawStringFormats

// 是否格式化註釋(這裏測試沒生效) ReflowComments
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */

// 是否對include 排序 SortIncludes
// 見上面的IncludeBlocks測試

// using xxx是否須要排序 SortUsingDeclarations
using std::cout;
using std::cin;


// 哪些操做後面須要加空格
// SpaceAfterCStyleCast: (int) i
// SpaceAfterLogicalNot: ! someExpression()
// SpaceAfterTemplateKeyword: template <int> void foo();
// SpaceBeforeAssignmentOperators: int a = 5
// SpaceBeforeCpp11BracedList: vector<int> { 1, 2, 3 }
// SpaceBeforeCtorInitializerColon: Foo::Foo() : a(a) {}
// SpaceBeforeInheritanceColon: class Foo : Bar {}
// SpaceBeforeParens: if (true) {}
// SpaceBeforeRangeBasedForLoopColon: for (auto v : values) {}
// SpaceInEmptyParentheses: f( ) // 括號中間有一個空格
// SpacesBeforeTrailingComments: // aaa //後面有一個空格再接註釋
// SpacesInAngles: static_cast< int >(arg) <>中間是否有空格
// SpacesInContainerLiterals: f({a : 1, b : 2, c : 3}); 這個C++沒用到
// SpacesInCStyleCastParentheses: x = ( int32 )y 類型轉換時,括號要不要加空格
// SpacesInParentheses: t f( Deleted & ) & = delete; 括號要不要加空格,
// SpacesInSquareBrackets: int a[ 5 ]; 數組的中括號要不要加空格

// 使用哪一個標準 Standard

// tab寬度 TabWidth

// 是否用tab縮進 UseTab

// TODO: 不能強制if換行時加大括號
if (true)
    a+ = bbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccc + ddddddddddddddddddddddd;
View Code

我本身用的配置github

# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
# dump by clang-format --dump-config

---
Language:        Cpp
# BasedOnStyle:  LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands:   true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine:  Inline
AllowShortLambdasOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: Allman
BraceWrapping:
  AfterCaseLabel:  true
  AfterClass:      true
  AfterControlStatement: true
  AfterEnum:       true
  AfterFunction:   true
  AfterNamespace:  true
  AfterObjCDeclaration: false
  AfterStruct:     true
  AfterUnion:      true
  AfterExternBlock: true
  BeforeCatch:     true
  BeforeElse:      true
  IndentBraces:    true
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakBeforeBinaryOperators:  NonAssignment
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit:     80
CommentPragmas:  '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IncludeBlocks:   Preserve
IncludeCategories:
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: BeforeHash
IndentWidth:     4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 10
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments:  true
SortIncludes:    false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
StatementMacros:
  - Q_UNUSED
  - QT_REQUIRE_VERSION
TabWidth:        4
UseTab:          Never
...

格式化後的效果算法

/* 測試clang-format格式化效果
 */

class Test
{
    // 對齊這個public修飾符 AccessModifierOffset: -2
public:
};

// 括號斷行後參數對齊方式 AlignAfterOpenBracket
void ttttttt(int aaaaaaa, int bbbbbbbbb, int ccccccccc, int ddddddddd,
             int eeeeeeeeeeeeeee, int ffffffffffffffffffffffffffffffff)
{
}
// TODO: 不能實現下面的對齊方式
// 1. 下一行長度大於上一行
// 2. 換行縮進爲4格
//
void ttttttt(int aaaaaaa, int bbbbbbbbb, int ccccccccc, int ddddddddd,
             int eeeeeeeeeeeeeee, int ffffffffffffffffffffffffffffffff)
{
}

// 賦值時等號對齊 AlignConsecutiveAssignments
int aaaaaaa = 222222222;
int b       = 7;
int ccccc   = 99999;

// 變量名左對齊,應該和上面的衝突 AlignConsecutiveDeclarations
int aaaa        = 12;
float b         = 23;
std::string ccc = 23;

// 宏對齊 AlignConsecutiveMacros
#define SHORT_NAME       42
#define LONGER_NAME      0x007f
#define EVEN_LONGER_NAME (2)
#define foo(x)           (x * x)
#define bar(y, z)        (y + z)

// 斷行符的對齊 AlignEscapedNewlines
#define A     \
    int aaaa; \
    int b;    \
    int dddddddddd

// 運算變量對齊 AlignOperands
int sum = aaaaaaaaaaa + bbbbbbbbbb + ccccccccccccccccccc + ddddddddddddddd
          + eeeeeeeeeee + fff;

// 是否對齊行尾註釋 AlignTrailingComments
int x;             // test xxxxxxxx
int yyyyyyyyyyyyy; // test yyyyyyyyy
int zzzz;          // test zzzzzzz

// 調用函數時,參數的斷行方式 AllowAllArgumentsOnNextLine
looooooooooooooooooooooooooooooooooooooooooooooong_call(aaaaaaaaaa, bbbbbbbb,
                                                        cccccccc);

// 構造函數初始化列表斷行方式 AllowAllConstructorInitializersOnNextLine
class LongInitializers
{
public:
    LongInitializers()
        : aaaaaaaaaaaaaaaaa(1), bbbbbbbbbbbbbbbbbb(2), ccccccccccccc(3)
    {
    }
};

// 函數聲明時參數的斷行方式 AllowAllParametersOfDeclarationOnNextLine
// 上面的AlignAfterOpenBracket優先級更高,會影響這個
int ddddddddddddddddddddddddddddddddddddddddd(int aaaaaa, int bbbbbbb,
                                              int cccccccccccc);

// 是否把簡短的代碼合併成一行 AllowShortBlocksOnASingleLine
// 這個沒生效,並且和文檔也對不上了。文檔有好幾個值,配置只接受 true flase
if (a > b) a++;
while (true)
{
    a++;
    b++;
};

// switch的case是否合併成一行 AllowShortCaseLabelsOnASingleLine
switch (type)
{
case 1: a++; break;
}

// 簡短的函數能不能放一行 AllowShortFunctionsOnASingleLine
class TestFuncOneLine
{
    void test() { a++; }
};

void not_one_line()
{
    a++;
}

// if 語句能不能放一行 AllowShortIfStatementsOnASingleLine
if (a > b)
{
    a++;
    if (0 == b) b++;
}
else
{
    b++;
    if (0 == a)
    {
        a = 0;
        b = 0;
    }
}

// lambda表達式能不能放一行 AllowShortLambdasOnASingleLine
void lambda_one_line()
{
    auto lambda = [](int a, int b) {
        return a > b;
    };

    // TODO: 這裏人換行
    sort(
        a.begin(), a.end(), ()[] { return x < y; });
}

// for等循環能不能放一行 AllowShortLoopsOnASingleLine
// 這個測試沒生效
while (true)
{
    a++;
};

do
{
    a++;
} while (0);
do
{
    a++
} while (0);

// TODO:這個宏總被展開
#define TEST(a) \
    do          \
    {           \
        a++     \
    } while (0)

// 函數聲明 返回類型以後要不要斷行 AlwaysBreakAfterDefinitionReturnType
int test() {} // 不換行
int test() {} // 換行

// 函數實現 返回類型以後要不要斷行 AlwaysBreakAfterReturnType
int test()
{
    a++;
}

// 字符串換行時,等號後面要不要換行 AlwaysBreakBeforeMultilineStrings
aaaa = "bbbb" // 換行
       "cccc";

aaaa = "bbbb" // 不換行
       "cccc";

// 模板聲明是否換行 AlwaysBreakTemplateDeclarations
template <typename T> T foo() {}
template <typename T>
T foo(int aaaaaaaaaaaaaaaaaaaaa, int bbbbbbbbbbbbbbbbbbbbb)
{
}

// 調用函數時,參數是否獨佔一行 BinPackArguments
func(a, b, c);

funcccccccccccccccccccccccccc(aaaaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbb,
                              ccccccccccccccccc, cccccccccccccccccccc);

// 聲明或者實現函數時,參數是否獨佔一行 BinPackParameters
int funcccccccccccccccccccccccccc(aaaaaaaaaaaaaaaaaaaaaaaaaaaa,
                                  bbbbbbbbbbbbbbbbbb, ccccccccccccccccc,
                                  cccccccccccccccccccc);

// 控制大括號的斷行方式 BraceWrapping
// BreakBeforeBraces的值爲Custom纔有效
class foo
{
};

// 運算符(=、+、、*等)換行方式 BreakBeforeBinaryOperators
LooooooooooongType loooooooooooooooooooooongVariable =
    someLooooooooooooooooongFunction();

bool value = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                     + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                 == aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
             && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
                    > ccccccccccccccccccccccccccccccccccccccccc;

// 大括號換行方式 BreakBeforeBraces

// 雙目運算符的斷行方式 BreakBeforeTernaryOperators
int a = a > b ? a : b;
veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongDescription
    ? firstValue
    : SecondValueVeryVeryVeryVeryLong;

// 初始化列表換行方式 BreakConstructorInitializers
// 繼承的換行方式 BreakInheritanceList
class Tttttttttttttttttttttttttttttttttttttttttttttttttttttest : Aaaaaa, Bbbbbbbbb
{
public:
    Tttttttttttttttttttttttttttttttttttttttttttttttttttttest()
        : Aaaaaa(), Bbbbbbbbb()
    {
    }
};

// 字符串是否容許換行
std::string str = "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
                  "ooooooooooooong str";

// 單行字符數 ColumnLimit

// 控制註釋中哪些內容不容許換行的正則 CommentPragmas

// 是否合併命名空間到一行 CompactNamespaces
namespace Foo
{
namespace Bar
{ // 合併
}
} // namespace Foo
// 不合並
namespace Foo
{
namespace Bar
{
}
} // namespace Foo

// 初始化列表是否全放到一行 ConstructorInitializerAllOnOneLineOrOnePerLine
SomeClass::Constructor()
    : aaaaaaaa(aaaaaaaa), aaaaaaaa(aaaaaaaa), aaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaaaa)
{
    return 0;
}

// 初始化列表換行時,縮進的寬度 ConstructorInitializerIndentWidth
// 發生換行時,縮進的寬度 ContinuationIndentWidth

// 大括號數組是否有空格 Cpp11BracedListStyle
vector<int> x{1, 2, 3, 4}; // cpp11風格,沒有
vector<int> x{1, 2, 3, 4}; //// 引用和指針的對齊方式 DerivePointerAlignment
// 不知道這個是用來幹啥的

// 徹底不格式化(爲啥會有這個選項,不格式化不調用不就好了麼) DisableFormat

// 自動根據當前文件其餘地方的格式來格式化函數參數 ExperimentalAutoDetectBinPacking
// 好比說其餘地方的參數是每一個參數佔一行,那它決定按每一個參數佔一行來格式化
// 這個是實驗性的

// 是否自動添加命名空間結束註釋 FixNamespaceComments
namespace a
{
foo();
} // namespace a

// 一些第三方的foreach循環宏定義,好比QT的 ForEachMacros

// include的合併方式 IncludeBlocks
// 通常按名字來排,按空行分組(由於有些順序是特定的),注意下面有個 SortIncludes 選項
#include "b.h"

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

// include 優先級 IncludeCategories
// 當上面的IncludeBlocks設置爲Regroup,會把include所有排序,排序規則就按這個來

// include規則 ,和上面的差很少 IncludeIsMainRegex

// switch的case縮進 IndentCaseLabels
switch (a)
{
case 1: break; // 不縮進
case 2: break; // 縮進
}

// togo標籤是否縮進,文檔裏有,程序不認這個選項了 IndentGotoLabels

// 多層宏定義鑲嵌時,縮進方式 IndentPPDirectives
#if FOO
    #if BAR
        #include <foo>
    #endif
#endif

// 縮進寬度 IndentWidth

// 當返回類型和函數名斷行時,函數名是否縮進 IndentWrappedFunctionNames
// 真有這麼長的類型和函數名嗎?
LoooooooooooooooooooooooooooooooooooooooongReturnType
LoooooooooooooooooooooooooooooooongFunctionDeclaration();

// 代碼塊開始時,要不要空一行 KeepEmptyLinesAtTheStartOfBlocks
// 這個沒生效,多是須要大括號在行尾的Java風格纔有效
if (true)
{

    test(); // 上面空一行
}

// 指定格式化提哪一個語言 Language

// 匹配一對開始和結束的宏 MacroBlockBegin MacroBlockEnd
NS_MAP_BEGIN
foo();
NS_MAP_END

// 容許連續空多少行 MaxEmptyLinesToKeep
if (true)
{
    a++;

    b++;
}

// 命名空間裏的代碼是否縮進 NamespaceIndentation
namespace out
{
int i; // 不縮進
namespace in
{
int i; // 縮進
}
} // namespace out

// 表示命名空間的宏(我用不着,暫時不測試) NamespaceMacros

// 這幾個Penalty暫時不知道是啥,下面的連接有討論
// https://stackoverflow.com/questions/26635370/in-clang-format-what-do-the-penalties-do
// 大概意思是當恰好超過行寬,但又不超幾個字符時,若是斷行
// 我試了下PenaltyBreakBeforeFirstCallParameter = 19, PenaltyExcessCharacter = 10
// 時,下面那個函數調用就不換行。但這個數值是根據算法來的,算法把各類因素給定一些值,得
// 出不一樣換行時的penalty值,其大小沒有標準可參考

// PenaltyBreakAssignment
auto loooooooooooooooooooooooooooooooooooooooooooooooooooooooooosong_var =
    "abc";
// PenaltyBreakBeforeFirstCallParameter
Namespaces::Are::Pervasive::SomeReallyVerySuperDuperLooooooooongFunctionName(args);
// PenaltyBreakComment
// PenaltyBreakFirstLessLess
// PenaltyBreakString
// PenaltyBreakTemplateDeclaration
// PenaltyExcessCharacter
// PenaltyReturnTypeOnItsOwnLine

// 指針的*號放哪 PointerAlignment
int *a; // left
int *a; // right

// 匹配字符串中代碼的正則,若是匹配到,字符串的代碼將會被格式化 RawStringFormats

// 是否格式化註釋(這裏測試沒生效) ReflowComments
/* second veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongComment with plenty of information */

// 是否對include 排序 SortIncludes
// 見上面的IncludeBlocks測試

// using xxx是否須要排序 SortUsingDeclarations
using std::cout;
using std::cin;

// 哪些操做後面須要加空格
// SpaceAfterCStyleCast: (int) i
// SpaceAfterLogicalNot: ! someExpression()
// SpaceAfterTemplateKeyword: template <int> void foo();
// SpaceBeforeAssignmentOperators: int a = 5
// SpaceBeforeCpp11BracedList: vector<int> { 1, 2, 3 }
// SpaceBeforeCtorInitializerColon: Foo::Foo() : a(a) {}
// SpaceBeforeInheritanceColon: class Foo : Bar {}
// SpaceBeforeParens: if (true) {}
// SpaceBeforeRangeBasedForLoopColon: for (auto v : values) {}
// SpaceInEmptyParentheses: f( ) // 括號中間有一個空格
// SpacesBeforeTrailingComments: // aaa //後面有一個空格再接註釋
// SpacesInAngles: static_cast< int >(arg) <>中間是否有空格
// SpacesInContainerLiterals: f({a : 1, b : 2, c : 3}); 這個C++沒用到
// SpacesInCStyleCastParentheses: x = ( int32 )y 類型轉換時,括號要不要加空格
// SpacesInParentheses: t f( Deleted & ) & = delete; 括號要不要加空格,
// SpacesInSquareBrackets: int a[ 5 ]; 數組的中括號要不要加空格

// 使用哪一個標準 Standard

// tab寬度 TabWidth

// 是否用tab縮進 UseTab

// TODO: 不能強制if換行時加大括號
if (true)
    a + = bbbbbbbbbbbbbbbbbbbbb + ccccccccccccccccccccccc
          + ddddddddddddddddddddddd;
View Code

整體來講,clang-format表現出色。可是有一些小地方不太滿意shell

// 爲何不是下一行比上一行長
void test(int aaaaaaaaaaaaaa, int bbbbbbbbbbb,
          int ccccccccccccccccc);

// if換行不能強制換加大括號
if (true)
    a ++;

// 這裏爲何要換行
sort(
   a.begin(), a.end(), ()[] { return x < y; });

  另外,clang-format不能指定配置文件的路徑,而我不喜歡把這些文件和源代碼文件放在一塊兒,爲此還寫了個批量格式化的腳本。上面的例子只是寫來測試,要看clang-format的格式化效果,直接在github上看對應項目的代碼便可,好比Linux kerneljson

  uncrustify這工具如今在github維護,也是比較活躍,但我沒見過用這個工具格式化的項目,並且上面也給出了格式化的效果,這裏就再也不測試了。數組

  astyle在sourceforge上,更新並不頻繁,格式化選項比較少,好比說沒有提提供宏定義對齊等。app

  不過有趣的是,uncrustify和astyle都提供了強制給if加大括號的選項,惟獨clang-format沒有。框架

  使用工具後,好處是代碼風格統一了,不用管其餘人寫代碼的風格怎麼樣。只要風格和工具格式化出來的不同,就不容許提交(好比CI自動運行clang-format,若是提交的代碼與clang-format格式化出來的不一致,則拒絕合併代碼)。壞處是手寫基本寫不出來這樣的代碼了,基本都須要工具格式化後才能提交,好在如今不少編輯器都提供format on save選項,開啓便可。

  不過,這只是格式化的問題,其實代碼風格中更大的問題是大小寫問題,好比大駝峯的寫法和linux下劃線的寫法,這個目前尚未發現對應的工具。Google出過一個工具,可是效果並非太好,https://github.com/google/styleguide/tree/gh-pages/cpplint

相關文章
相關標籤/搜索