Qt 拾遗 009 Qt Creator 中配置 clang-format 工具

使用 Qt Creator 插件 beautifier 配置 clang-format 工具管理项目代码整体风格

1. 问题的提出

我们平时在进行团队合作开发的时候,需要注意代码的格式风格,虽然可以通过文档约定,要求成员遵守一定的编码规范。但是在实际开发中这种约定因为每个人的认识程度不同,是很难实现真正的风格统一的,能否通过工具来管理代码风格,实现真正的统一呢?答案是肯定的,这就是本文推荐的 clang-format

2. clang-format 简介

clang-format 既是一个库,也是一个独立的工具,其目标是根据可配置的样式指南自动重新格式化 C++ 源文件。

clang-format 可用于格式化(排版)多种不同语言的代码,其自带的排版格式主要有:

  1. LLVM 符合LLVM编码标准的样式
  2. Google 符合Google C++ 风格指南 的样式
  3. Chromium 符合Chromium风格指南的样式
  4. Mozilla 符合Mozilla风格指南的样式
  5. WebKit 符合WebKit风格指南的样式

但是自带的这几种排版格式可能并不满足个人编码习惯的全部要求,clang-format 也提供了使用自定义排版格式配置文件进行排版的功能。

如果在 Ubuntu 中安装了 clang-format,则可以使用以下命令导出上述 5 种自带的排版格式,并且可以对其中某一个导出的文件进行修改,实现自定义格式化:

1
clang-format -style=格式名 -dump-config > your_filename

其中,格式名的取值可以为 llvm、google、chromium、mozilla、webkit 中的任一种;文件名可以取任何名字,一般取 .clang-format 或 _clang-format,因为自定义的排版格式文件只有取这两种名字之一,才能被 clang-format 识别。在 Ubuntu 下以点开头命名的文件是隐藏文件,所以导出为 .clang-format 后,在文件夹中看不到该文件,可以使用快捷键 Ctrl+h 显示隐藏文件。

3. 准备工作和命令行方式使用方法

  1. Ubuntu 中安装 clang-format
1
sudo apt-get install clang-format
  1. 格式化风格可使用内置风格或使用参数文件指定风格(-style 参数指定所选风格,可选项为 LLVM、Google、Chromium、Mozilla、WebKit 和 file,其中 file 指定参数文件。-i 表示将格式化后的内容写入原文件):
1
clang-format -style=LLVM -i test.cc

  1. clang-format 还提供一个 clang-format-diff.py 脚本,用来格式化 patch,code review 提交代码前,可以先跑一下这个脚本(格式化最新的 commit,并直接在原文件上修改):
1
git diff -U0 HEAD^ | clang-format-diff -i -p1

4. 在 Qt Creator 中使能 beautifier 插件

在 Qt Creator 的菜单中打开“帮助>关于插件”,之后在弹出窗口中勾选 “Beautifier”插件,之后关闭窗口。

Qt Creator 中使能 beautifier 插件操作

使能的 beautifier 插件,需要重新启动 Qt Creator 才能被装载,所以做好项目保存之后,重启 Qt Creator,之后,就可以在“工具”菜单下看到“Beautifier”选项,表示使能成功。

5. 在 Qt Creator 中配置 clang-format

  1. 使用默认的一些风格设置 (选择 File 风格,此时 Ctrl+s 保存文件时就会自动格式化代码)

默认的File风格设置

  1. 使用指定的自定义的风格的设置,以使用Qt Creator 源码中的 .clang-format 文件为例,添加自定义风格设置

使用自定义的风格的设置步骤1

使用自定义的风格的设置步骤2

6. clang-format 一些常用选项及含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
Language:   Cpp
# BasedOnStyle: LLVM
# 访问说明符(public、private等)的偏移
AccessModifierOffset:   -4
# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
AlignAfterOpenBracket:  Align
# 连续赋值时,对齐所有等号
AlignConsecutiveAssignments:    true
# 连续声明时,对齐所有声明的变量名
AlignConsecutiveDeclarations:   true
# 左对齐逃脱换行(使用反斜杠换行)的反斜杠
AlignEscapedNewlinesLeft:   true
# 水平对齐二元和三元表达式的操作数
AlignOperands:  true
# 对齐连续的尾随的注释
AlignTrailingComments:  true
# 允许函数声明的所有参数在放在下一行
AllowAllParametersOfDeclarationOnNextLine:  true
# 允许短的块放在同一行
AllowShortBlocksOnASingleLine:  false
# 允许短的case标签放在同一行
AllowShortCaseLabelsOnASingleLine:  false
# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
AllowShortFunctionsOnASingleLine:   Empty
# 允许短的if语句保持在同一行
AllowShortIfStatementsOnASingleLine:    false
# 允许短的循环保持在同一行
AllowShortLoopsOnASingleLine:   false
# 总是在定义返回类型后换行(deprecated)
AlwaysBreakAfterDefinitionReturnType:   None
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
#   AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
AlwaysBreakAfterReturnType: None
# 总是在多行string字面量前换行
AlwaysBreakBeforeMultilineStrings:  false
# 总是在template声明后换行
AlwaysBreakTemplateDeclarations:    false
# false表示函数实参要么都在同一行,要么都各自一行
BinPackArguments:   true
# false表示所有形参要么都在同一行,要么都各自一行
BinPackParameters:  true
# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
BraceWrapping:  
  # class定义后面
  AfterClass:   false
  # 控制语句后面
  AfterControlStatement:    false
  # enum定义后面
  AfterEnum:    false
  # 函数定义后面
  AfterFunction:    false
  # 命名空间定义后面
  AfterNamespace:   false
  # ObjC定义后面
  AfterObjCDeclaration: false
  # struct定义后面
  AfterStruct:  false
  # union定义后面
  AfterUnion:   false
  # catch之前
  BeforeCatch:  true
  # else之前
  BeforeElse:   true
  # 缩进大括号
  IndentBraces: false
# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
BreakBeforeBinaryOperators: NonAssignment
# 在大括号前换行: Attach(始终将大括号附加到周围的上下文), Linux(除函数、命名空间和类定义,与Attach类似),
#   Mozilla(除枚举、函数、记录定义,与Attach类似), Stroustrup(除函数定义、catch、else,与Attach类似),
#   Allman(总是在大括号前换行), GNU(总是在大括号前换行,
#   并对于控制语句的大括号增加额外的缩进), WebKit(在函数前换行), Custom
#   注:这里认为语句块也属于函数
BreakBeforeBraces:  Custom
# 在三元运算符前换行
BreakBeforeTernaryOperators:    true
# 在构造函数的初始化列表的逗号前换行
BreakConstructorInitializersBeforeComma:    false
# 每行字符的限制,0表示没有限制
ColumnLimit:    200
# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
CommentPragmas: '^ IWYU pragma:'
# 构造函数的初始化列表要么都在同一行,要么都各自一行
ConstructorInitializerAllOnOneLineOrOnePerLine: false
# 构造函数的初始化列表的缩进宽度
ConstructorInitializerIndentWidth:  4
# 延续的行的缩进宽度
ContinuationIndentWidth:    4
# 去除C++11的列表初始化的大括号{后和}前的空格
Cpp11BracedListStyle:   false
# 继承最常用的指针和引用的对齐方式
DerivePointerAlignment: false
# 关闭格式化
DisableFormat:  false
# 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental)
ExperimentalAutoDetectBinPacking:   false
# 需要被解读为foreach循环而不是函数调用的宏
ForEachMacros:  [ foreach, Q_FOREACH, BOOST_FOREACH ]
# 对#include进行排序,匹配了某正则表达式的#include拥有对应的优先级,
#   匹配不到的则默认优先级为INT_MAX(优先级越小排序越靠前),
#   可以定义负数优先级从而保证某些#include永远在最前面
IncludeCategories:
  - Regex:  '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:   2
  - Regex:  '^(<|"(gtest|isl|json)/)'
    Priority:   3
  - Regex:  '.*'
    Priority:   1
# 缩进case标签
IndentCaseLabels:   false
# 缩进宽度
IndentWidth:    4
# 函数返回类型换行时,缩进函数声明或函数定义的函数名
IndentWrappedFunctionNames: false
# 保留在块开始处的空行
KeepEmptyLinesAtTheStartOfBlocks:   true
# 开始一个块的宏的正则表达式
MacroBlockBegin:    ''
# 结束一个块的宏的正则表达式
MacroBlockEnd:  ''
# 连续空行的最大数量
MaxEmptyLinesToKeep:    1
# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
NamespaceIndentation:   Inner
# 使用ObjC块时缩进宽度
ObjCBlockIndentWidth:   4
# 在ObjC的@property后添加一个空格
ObjCSpaceAfterProperty: false
# 在ObjC的protocol列表前添加一个空格
ObjCSpaceBeforeProtocolList:    true
# 在call(后对函数调用换行的penalty
PenaltyBreakBeforeFirstCallParameter:   19
# 在一个注释中引入换行的penalty
PenaltyBreakComment:    300
# 第一次在<<前换行的penalty
PenaltyBreakFirstLessLess:  120
# 在一个字符串字面量中引入换行的penalty
PenaltyBreakString: 1000
# 对于每个在行字符数限制之外的字符的penalty
PenaltyExcessCharacter: 1000000
# 将函数的返回类型放到它自己的行的penalty
PenaltyReturnTypeOnItsOwnLine:  60
# 指针和引用的对齐: Left, Right, Middle
PointerAlignment:   Left
# 允许重新排版注释
ReflowComments: true
# 允许排序#include
SortIncludes:   true
# 在C风格类型转换后添加空格
SpaceAfterCStyleCast:   false
# 在赋值运算符之前添加空格
SpaceBeforeAssignmentOperators: true
# 开圆括号之前添加一个空格: Never, ControlStatements, Always
SpaceBeforeParens:  ControlStatements
# 在空的圆括号中添加空格
SpaceInEmptyParentheses:    false
# 在尾随的评论前添加的空格数(只适用于//)
SpacesBeforeTrailingComments:   2
# 在尖括号的<后和>前添加空格
SpacesInAngles: true
# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
SpacesInContainerLiterals:  true
# 在C风格类型转换的括号中添加空格
SpacesInCStyleCastParentheses:  true
# 在圆括号的(后和)前添加空格
SpacesInParentheses:    true
# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
SpacesInSquareBrackets: true
# 标准: Cpp03, Cpp11, Auto
Standard:   Cpp11
# tab宽度
TabWidth:   4
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
UseTab: Never

7. clang-format 官方文档

clang-format 官方文档中针对每个选项都会有例子代码,可以访问查阅:clang-format 官方文档

8. 参考文章

本文编写过程中参考了以下文章,感谢这些作者的无私分享

  • Clang-Format格式化选项介绍
  • clang-format规范说明书
  • Linux下Clang-format代码格式化
  • QtCreator 使用beautifier插件配置Artistic Style

9. 本文的 PDF 版本下载

链接: https://pan.baidu.com/s/15A6oeJJGhyqTcnvGF0a31g
提取码: r58k