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
);
}


4150

被折叠的 条评论
为什么被折叠?



