我本身寫的業餘框架已告一段落,主體功能已完成,剩下的就是優化。第一個要優化的,就是代碼格式。我一直是用編輯器寫代碼的,從以前的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;
我本身用的配置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;
整體來講,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 kernel。json
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。