zoukankan      html  css  js  c++  java
  • 测试clang-format的格式化效果

      我自己写的业余框架已告一段落,主体功能已完成,剩下的就是优化。第一个要优化的,就是代码格式。我一直是用编辑器写代码的,从之前的UltraEdit到notepad++到sublime text,再到现在的VS Code。由于代码都是我一个人写,风格也比较统一,虽然说不上美观,但至少说得过去。但寻思着以后万一有人要用这代码,总得有个较为通用的代码风格才行,而且我也不太可能去人工约束别人怎么写,那就用工具吧。

      C++不像Java、C#、TypeScript这些语言,他们都有较为通用的代码风格标准,比较通用的IDE,基本是自带代码格式化,因此整体上来说比较容易统一。但C++就没有,比如我在公司是用Visual Studio,在家有时候用的VS Code,有时候用的Qt。现在流行的C++代码格式化工具,大概有3个:clang-format、uncrustify、astyle。

      clang-format是随LLVM项目而来的后起之秀,也是这次测试的重点。原因是它的开发现在是最活跃的,格式化选项是最多的,集成也是最多的(VS2017以后有集成,VS Code有插件,Qt在新版本中已经集成)。我花了点时间,尝试了解clang-format的配置,并做了些测试。

    /* 测试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

    我自己用的配置

    # 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表现出色。但是有一些小地方不太满意

    // 为什么不是下一行比上一行长
    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

      uncrustify这工具现在在github维护,也是比较活跃,但我没见过用这个工具格式化的项目,而且上面也给出了格式化的效果,这里就不再测试了。

      astyle在sourceforge上,更新并不频繁,格式化选项比较少,比如说没有提提供宏定义对齐等。

      不过有趣的是,uncrustify和astyle都提供了强制给if加大括号的选项,唯独clang-format没有。

      使用工具后,好处是代码风格统一了,不用管其他人写代码的风格怎么样。只要风格和工具格式化出来的不一样,就不允许提交(比如CI自动运行clang-format,如果提交的代码与clang-format格式化出来的不一致,则拒绝合并代码)。坏处是手写基本写不出来这样的代码了,基本都需要工具格式化后才能提交,好在现在很多编辑器都提供format on save选项,开启即可。

      不过,这只是格式化的问题,其实代码风格中更大的问题是大小写问题,比如大驼峰的写法和linux下划线的写法,这个目前还没有发现对应的工具。Google出过一个工具,但是效果并不是太好,https://github.com/google/styleguide/tree/gh-pages/cpplint

  • 相关阅读:
    Delphi的几个跨平台小游戏例子。
    Delphi判断某个类是否实现了某个接口
    Delphi RAD Server 应用服务基础平台
    Delphi XE10.1 引用计数
    运行Delphi XE10的MongoDB例程,测试Delphi插入记录性能
    在Windows下编译mongo-c-driver 1.3.x
    Delphi 高效读写锁
    Delphi XE10在 Android下调用静态库a文件
    Delphi 调试连接 任意Android手机/平板/盒子
    Some cool FireMonkey multi-device components
  • 原文地址:https://www.cnblogs.com/coding-my-life/p/11829654.html
Copyright © 2011-2022 走看看