使用clang-format格式化c++代码

1 前言

clang-format是一个可以格式化代码的工具,本文将介绍如何在命令行中使用clang-format来格式化c++代码,然后会给出一种在c++工程中利用脚本来格式化所有c++文件的工作流程,最后将介绍clang-format中的配置字段,根据自定义的配置字段可以创建自己的代码格式风格。

2 clang-format的使用

查看clang-format版本信息的命令:

clang-format --version

输出结果为安装的clang-format版本:

clang-format version 17.0.6

假如有一个需要格式化的文件test.cpp

    #include <iostream>

int main() 
{
int a = 0;
int b = 0;

              return 0;
}

只需要在test.cpp文件所在的目录打开终端,并输入以下命令:

clang-format -style=llvm -i test.cpp 

test.cpp文件即可被格式化为如下:

#include <iostream>

int main() {
  int a = 0;
  int b = 0;

  return 0;
}

命令行中的-style指定了目标格式的风格,可以选择llvm、google、gnu等格式风格。

另外,我们也可以在test.cpp目录下放置一个.clang-format文件

.
├── .clang-format
└── test.cpp

然后使用命令

clang-format -i test.cpp 

即可将test.cpp格式化为目标格式。需要注意的是这种方式不仅对.clang-format文件所在的目录有效,对其子目录的文件也可以格式化。我们可以使用以下命令来快速生成一个.clang-format文件:

clang-format -style=llvm -dump-config > .clang-format

然后可以根据需要修改这个文件形成我们自己的代码格式化风格。

3 在c++项目中使用clang-format

假设有一个c++项目目录结构如下图所示:

.
├── .clang-format
├── Script
│   └── FormatFile.sh
├── Source
│   ├── App
│   │   └── main.cpp
│   ├── LibA
│   │   ├── LibA.cpp
│   │   └── LibA.h
│   └── LibB
│       ├── LibB.cpp
│       └── LibB.h
└── format.sh

其中Source目录包含了cpp源文件,.clang-format文件是自定义的格式风格文件,Script目录包含了项目使用的脚本文件,Script中的FormatFile.sh提供了格式化某个目录下所有文件的接口,实现如下:

function FormatFile()
{
	for file in $(ls $1)
	do 
		if [ -d $1"/"$file ]; then
			FormatFile $1"/"$file
		else 
			if [[ $1"/"$file = *.h  || $1"/"$file = *.cpp ]]; then
				clang-format -i $1"/"$file
				echo format $1"/"$file
			fi
		fi
	done
}

format.sh脚本通过调用FormatFile.sh中的接口对整个项目中的c++文件进行格式化,每次修改或者提交代码时都可以运行这个脚本,其实现如下:

source ./Script/FormatFile.sh

echo -e "--- start clang-format ---"

FormatFile Source

echo -e "--- finish clang-format ---"

对FormatFile接口传入Source参数,即可将Source目录下的所有c++文件格式化

4 自定义配置字段

我们可以通过修改.clang-format文件中的字段来实现自己的代码风格,可参考clang-format的官方文档: 

https://clang.llvm.org/docs/ClangFormatStyleOptions.html

本节内容基于clang-format 17

4.1 Language

指定编程语言:

Language:        Cpp

4.2 AccessModifierOffset

访问修饰符的缩进(例如public,private等)

AccessModifierOffset: -4
// 修饰符缩进为-4(假设缩进参数为4)

class MyClass {
public:
    MyClass();

private:
    int m_Num = 0;
};

4.3 AlignAfterOpenBracket

当需要换行时,控制参数的对齐风格

// AlignAfterOpenBracket: Align
someLooooooooooooongFunction(LoooooooooooooooooooooooooooooooooooooongArgument1,
                             LoooooooooooooooooooooooooooooooooooooongArgument2,
                             Argument3);

// AlignAfterOpenBracket: DontAlign
someLooooooooooooongFunction(LoooooooooooooooooooooooooooooooooooooongArgument1,
    LoooooooooooooooooooooooooooooooooooooongArgument2, Argument3);

// AlignAfterOpenBracket: AlwaysBreak
someLooooooooooooongFunction(
    LoooooooooooooooooooooooooooooooooooooongArgument1,
    LoooooooooooooooooooooooooooooooooooooongArgument2, Argument3);

// AlignAfterOpenBracket: BlockIndent
someLooooooooooooongFunction(
    LoooooooooooooooooooooooooooooooooooooongArgument1,
    LoooooooooooooooooooooooooooooooooooooongArgument2, Argument3
);

4.4 AlignArrayOfStructures

对结构数组初始化时,字段排列成列的方式

// None
AlignArrayOfStructures: None

int Nums[4][3] = {
    {56, 23, 1557}, {-1, 93463, 422}, {7, 5, 10}, {7801, 15, 8010}};


// 按左对齐
AlignArrayOfStructures: Left

int Nums[4][3] = {
    {56,   23,    1557},
    {-1,   93463, 422 },
    {7,    5,     10  },
    {7801, 15,    8010}
};


// 按右对齐
AlignArrayOfStructures: Right

int Nums[4][3] = {
    {  56,    23, 1557},
    {  -1, 93463,  422},
    {   7,     5,   10},
    {7801,    15, 8010}
};

4.5 AlignConsecutiveAssignments

连续赋值语句的对齐方式,有Enabled、AcrossEmptyLines、AcrossComments、AlignCompound、PadOperators五个选项,分别可以有true和false两个值

Enabled: 是否开启对齐

// Enabled: false
int a = 0;
int bbbbb = 1;
int cc = 5;

// Enabled: true
int a     = 0;
int bbbbb = 1;
int cc    = 5;

AcrossEmptyLines: 是否跨空行对齐

// AcrossEmptyLines: false
int a     = 0;
int bbbbb = 1;
int cc    = 5;

int somelongname = 2;
double c         = 3;


// AcrossEmptyLines: true
int a            = 0;
int bbbbb        = 1;
int cc           = 5;

int somelongname = 2;
double c         = 3;

 AcrossComments: 是否跨注释对齐

// AcrossComments: false
int a   = 3;
int bbb = 10;
/* comment. */
double e    = 4;
float fffff = 4;


// AcrossComments: true
int a       = 3;
int bbb     = 10;
/* comment. */
double e    = 4;
float fffff = 4;

 AlignCompound: 复合赋值是否和等号赋值一起对齐

// AlignCompound: false
aaaaaa &= 2;
bb = 2;

// AlignCompound: true
aaaaaa &= 2;
bb      = 2;

PadOperators: 短赋值运算符号是否左填充到和长赋值运算符相同的长度

// AlignCompound需要为true

// PadOperators: false
a >>= 2;
bbb = 2;

a     = 2;
bbb >>= 2;

// PadOperators: true
a   >>= 2;
bbb   = 2;

a     = 2;
bbb >>= 2;

4.6 AlignConsecutiveBitFields 

连续位字段的对齐方式

// Enabled: false
struct MyStruct {
    int aaaa : 1;
    int b : 12;
    int ccc : 8;
};

// Enabled: true
struct MyStruct {
    int aaaa : 1;
    int b    : 12;
    int ccc  : 8;
};

4.7 AlignConsecutiveDeclarations

连续的声明语句的对齐方式

// AlignConsecutiveDeclarations: false
int aaaa = 12;
float b = 23;
std::string ccc;

// AlignConsecutiveDeclarations: true
int         aaaa = 12;
float       b = 23;
std::string ccc;

4.8 AlignConsecutiveMacros

连续的宏定义的对齐方式

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

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

4.9 AlignConsecutiveShortCaseStatements

用于控制连续的case语句的对齐方式,需要设置AllowShortCaseLabelsOnASingleLine为true

Enabled

// Enabled: false
switch (level)
{
case log::info: return "info:";
case log::warning: return "warning:";
default: return "";
}


// Enabled: true
switch (level)
{
case log::info:    return "info:";
case log::warning: return "warning:";
default:           return "";
}

AcrossEmptyLines

// AcrossEmptyLines: false
switch (level)
{
case 123:        return 123;
case 12'344'444: return 12'344'444;

case 234: return 234;
default:  return 234;
}

// AcrossEmptyLines: true
switch (level)
{
case 123:        return 123;
case 12'344'444: return 12'344'444;

case 234:        return 234;
default:         return 234;
}

AcrossComments

// AcrossComments: false
switch (level)
{
case 123:        return 123;
case 12'344'444: return 12'344'444;
//  A comment
case 234: return 234;
default:  return 234;
}

// AcrossComments: true
switch (level)
{
case 123:        return 123;
case 12'344'444: return 12'344'444;
//  A comment
case 234:        return 234;
default:         return 234;
}

 AlignCaseColons

// AlignCaseColons: false
switch (level)
{
case 123:        return 123;
case 12'344'444: return 12'344'444;
case 234:        return 234;
default:         return 234;
}

// AlignCaseColons: true
switch (level)
{
case 123       : return 123;
case 12'344'444: return 12'344'444;
case 234       : return 234;
default        : return 234;
}

4.10 AlignEscapedNewlines

转义换行符的对齐方式

// AlignEscapedNewlines: DontAlign
#define A \
    int aaaa; \
    int b; \
    int dddddddddd;

// AlignEscapedNewlines: Left
#define A     \
    int aaaa; \
    int b;    \
    int dddddddddd;

// AlignEscapedNewlines: Right
#define A                                                                      \
    int aaaa;                                                                  \
    int b;                                                                     \
    int dddddddddd;

4.11 AlignOperands

换行时,二元和三元操作符的对齐方式

// BreakBeforeBinaryOperators设置为All
// AlignOperands: DontAlign
int aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    + ccccccccccccccccccccccccccccccccccccccccc;

// AlignOperands: Align
int aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
                          + ccccccccccccccccccccccccccccccccccccccccc;

// AlignOperands: AlignAfterOperator
int aaaaaaaaaaaaaaaaaaa = bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
                        + ccccccccccccccccccccccccccccccccccccccccc;

4.12 AlignTrailingComments

控制尾部注释的对齐方式,有Kind和OverEmptyLines两个选项,Kind控制对齐的方式,OverEmptyLines控制对齐的空行数

// Kind: Leave
int a;    // comment
int abbbbbb;       // comment

// Kind: Always
int a;       // comment
int abbbbbb; // comment

// Kind: Never
int a; // comment
int abbbbbb; // comment

4.13 AllowAllArgumentsOnNextLine

函数调用需要换行时,是否允许把所有的参数放到下一行

// BinPackArguments设置为false
// AllowAllArgumentsOnNextLine: false
void func()
{
    dosomething();
    funccccccccccccccccccccc(
        LoooooooooooooooooooooooogArg1,
        LongArg2,
        LongArg2
    );
}

// AllowAllArgumentsOnNextLine: true
void func()
{
    dosomething();
    funccccccccccccccccccccc(
        LoooooooooooooooooooooooogArg1, LongArg2, LongArg2
    );
}

4.14 All

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lucy_stone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值