记录一下VSCode配置C++的内容吧。

必要软件

为了正常使用所有功能,除了针对VSCode的配置,需要单独安装下列软件,并把路径添加到PATH

  • Git
  • CMake
  • ninja

插件列表

首先列一下目前在VSCode上使用的,涉及到C++的核心插件:

  • C/C++ (Microsoft):微软官方默认的C/C++插件,禁用了代码提示,但是调试这些还需要用它,不能完全禁用
  • clangd (LLVM):LLVM官方提供的,当前代码提示的主力插件
  • CMake Tools (Microsoft):微软官方提供的CMake支持,提供CMake命令工具条和快捷指令,以及CMake语法高亮
  • CodeLLDB (Vadim Chugunov):lldb调试支持,非官方支持

还有两个不太重要的插件:

  • C/C++ Themes (Microsoft)
  • Better C++ Syntax (Jeff Hykin)

补充:通过clangd (LLVM) 插件安装的clangd虽然是最新版本,但是代码修正功能可能有问题,还是直接使用系统中的clangd更加可靠。

C/C++ 插件和 clangd 插件配置

主要的内容是禁用默认C/C++插件的主要功能,然后用clangd以及对应插件替换掉,并对clangd进行配置。

1
2
3
4
5
6
7
//[[C++]]
"C_Cpp.formatting": "disabled",
"C_Cpp.intelliSenseEngine": "disabled",
"C_Cpp.autocomplete": "disabled",
"C_Cpp.errorSquiggles": "disabled",
"C_Cpp.codeAnalysis.runAutomatically": false,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//[[clangd]]
"clangd.arguments": [
"--completion-style=detailed",
"--clang-tidy",
"--background-index",
"--enable-config",
"--header-insertion=never",
"--header-insertion-decorators",
"--fallback-style=LLVM",
],
"clangd.fallbackFlags": [
"-I${workspaceFolder}",
"-I${workspaceFolder}/include",
"-fsanitize=address",
"-fsanitize=undefined",
"-Wall",
"-Wextra",
"-Wshadow",
"-Wfatal-errors",
"-Wno-unused-variable",
"-Wno-unused-parameter",
// "-std=c++20"
],

CMaketools 插件配置

对CMake提供支持的插件配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//[[cmake]]
"cmake.configureOnOpen": false,
"cmake.autoSelectActiveFolder": false,
"cmake.configureOnEdit": false,
"cmake.options.statusBarVisibility": "compact",
"cmake.options.advanced": {
"cpack": {
"statusBarVisibility": "hidden"
},
"workflow": {
"statusBarVisibility": "hidden"
},
"debug": {
"statusBarVisibility": "hidden"
},
"ctest": {
"statusBarVisibility": "icon"
}
},
"cmake.preferredGenerators": [
"Ninja",
"Unix Makefiles",
"Visual Studio 17 2022"
],

如果 CMakeLists.json 插件在识别编译工具链时出现错误,可以直接删除,让它重新扫描配置(也可以手动修改,但是插件在重新扫描时会直接覆盖)

  • 在Windows中,配置文件位于~/AppData/Local/CMakeTools/cmake-tools-kits.json
  • 在WSL2中,配置文件位于~/.local/share/CMakeTools/cmake-tools-kits.json

C++ 调试配置

下面的部分是关于CMake项目调试的配置,早期这种配置必须存放在项目的.vscode/launch.json文件中, 现在已经支持在settings.json中添加默认配置了。

这三种配置使用的调试器分别是:

  • lldb
  • gdb
  • vsdbg

分别对应与clang,gcc和MSVC。

用它们调试对应的编译器得到的可执行程序通常是没问题的,但是混用是很可能有问题的:

  • 在Windows上:(clang是基于MSVC的,gcc则是基于MinGW)
    • 对于MSVC和clang生成的可执行文件,可以使用vsdbg或lldb调试
    • 对于gcc生成的可执行文件,只能使用gdb调试
    • 其它情况下无法调试,例如无法在端点中断
    • 如果clang也基于MinGW,那么对于gcc和clang生成的可执行文件,可以使用gdb或lldb调试
  • Linux:(clang的底层是基于gcc)
    • 对于gcc和clang生成的可执行文件,可以使用gdb或lldb调试
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
//**************************************************************************//
//[[launch]]
"launch": {
"configurations": [
{
"name": "lldb(CMake)",
"type": "lldb",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
},
{
"name": "gdb(CMake)",
"type": "cppdbg",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "PATH",
"value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
}
],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
{
"name": "vsdbg(CMake)",
"type": "cppvsdbg",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "PATH",
"value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
}
],
"console": "externalTerminal"
}
],
"compounds": []
},

在 Linux 上使用 gdb 调试时,调试结束可能会产生一条额外的信息

1
[1] + Done                       "/usr/bin/gdb" --interpreter=mi --tty=${DbgTerm} 0<"/tmp/Microsoft-MIEngine-In-ke1rzd3e.fgm" 1>"/tmp/Microsoft-MIEngine-Out-vqfwhzpa.2bs"

这并不是错误,而是 VSCode C++ 扩展的调试机制导致的。

clangd 配置

这里的clangd指的是专门的软件(通常在下载clang时附带下载),并不是VScode插件,关于clangd主要有三种配置文件:

  • .clangd:主配置文件;
  • .clangd-tidy:关于C++静态检查的配置文件;
  • .clangd-format:关于C++格式化检查的配置文件;

.clangd

clangd有时候比较蠢,我们可以通过在项目根目录添加.clangd配置文件来协助,例如它很可能在Windows上找不到正确的标准库头文件,而是会跳转到MSVC的标准库头文件,我们可以添加--include-directory=来指定。

.clangd 示例如下

1
2
3
4
5
6
7
8
CompileFlags:
Add:
- '-Wall'
- '-Wextra'
- '-Wshadow'
- '-Wno-unused-parameter'
- '-std=c++17'
- '--include-directory=/path/to/compiler/include'

有时候需要使用C语言项目,但是默认的配置是针对C++的,对于C语言项目可以使用下面的配置,重点是-xc选项

1
2
3
4
5
6
7
CompileFlags:
Remove: [-std=*]
Add: [-xc, -Wall]
Compiler: clang
Diagnostics:
ClangTidy:
Remove: readability-identifier-naming

除此之外,clangd主要会基于compile_commands.json文件进行代码提示,CMake项目可以用特定选项在build/中自动生成compile_commands.json

clangd 对于头文件的分析有时候很愚蠢,非要警告某些头文件没有被当前的文件实际使用,但是它其实有特别的用途(比如导出接口,或者就是clangd没有分析出来),真正将其移除又会导致编译失败, 此时可以使用下面的注释来关闭对应警告(IWYU:include-what-you-use),参考官方文档

1
2
#include "a.h"
#include "b.h" // IWYU pragma: keep

.clang-tidy

clang-tidy可以提供C++的静态语法检查,包括很多具体规则的检查(有的检查过于严格了,不能全部开启),通过.clang-tidy配置文件设置。

可以使用指定注释关闭特定位置的静态语法检查:

  • 关闭当前行的所有检查或特定检查

    1
    2
    xxx // NOLINT
    xxx // NOLINT(xxx)

  • 关闭下一行的所有检查或特定检查

    1
    2
    xxx // NOLINTNEXTLINE
    xxx // NOLINTNEXTLINE(xxx)

  • 关闭指定代码块的全部检查或特定检查

    1
    2
    3
    4
    5
    6
    7
    // NOLINTBEGIN
    xxx
    // NOLINTEND

    // NOLINTBEGIN(check-name)
    xxx
    // NOLINTEND(check-name)

.clang-tidy 模板如下,具体含义可以参考clang官方文档

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
Checks: 'bugprone-*,
-bugprone-easily-swappable-parameters,
cert-*,
-cert-env33-c,
-cert-dcl50-cpp,
-cert-err33-c,
clang-analyzer-*,
cppcoreguidelines-*,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-pro-type-cstyle-cast,
-cppcoreguidelines-owning-memory,
google-*,
-google-objc-*,
-google-readability-todo,
-google-readability-casting,
hicpp-*,
-hicpp-vararg,
llvm-*,
misc-*,
-misc-non-private-member-variables-in-classes,
-misc-unused-parameters,
-cppcoreguidelines-non-private-member-variables-in-classes,
modernize-*,
-modernize-loop-convert,
-modernize-use-nodiscard,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
-cppcoreguidelines-avoid-c-arrays,
-hicpp-avoid-c-arrays,
mpi-*,
performance-*,
-performance-unnecessary-value-param,
-performance-enum-size,
readability-*,
readability-identifier-naming,
-readability-braces-around-statements,
-hicpp-braces-around-statements,
-google-readability-braces-around-statements,
-readability-identifier-length,
-readability-redundant-control-flow,
-readability-magic-numbers,
-cppcoreguidelines-avoid-magic-numbers,
-readability-const-return-type,
-readability-math-missing-parentheses,
fuchsia-default-arguments-declarations,
-misc-use-internal-linkage,
'

CheckOptions:
- {key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE}
- {key: readability-identifier-naming.NamespaceCase, value: lower_case}
- {key: readability-identifier-naming.ClassCase, value: CamelCase}
- {key: readability-identifier-naming.StructCase, value: CamelCase}
- {key: readability-identifier-naming.EnumCase, value: CamelCase}
- {key: readability-identifier-naming.TypedefCase, value: CamelCase}
- {key: readability-identifier-naming.TypeAliasCase, value: CamelCase}
- {key: readability-identifier-naming.TemplateParameterCase, value: CamelCase}
- {key: readability-identifier-naming.ValueTemplateParameterCase, value: CamelCase}
- {key: readability-identifier-naming.ParameterCase, value: aNy_CasE}
- {key: readability-identifier-naming.MethodCase, value: lower_case}
- {key: readability-identifier-naming.PublicMethodCase, value: aNy_CasE}
- {key: readability-identifier-naming.MemberCase, value: lower_case}
- {key: readability-identifier-naming.PublicMemberCase, value: aNy_CasE}
- {key: readability-identifier-naming.PrivateMemberPrefix, value: m_}
- {key: readability-identifier-naming.FunctionCase, value: aNy_CasE}
- {key: readability-identifier-naming.VariableCase, value: aNy_CasE}
- {key: readability-identifier-naming.ConstexprVariableCase, value: aNy_CasE}
- {key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor, value: true}
- {key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions, value: true}
- {key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted, value: true}
- {key: hicpp-special-member-functions.AllowSoleDefaultDtor, value: true}
- {key: hicpp-special-member-functions.AllowMissingMoveFunctions, value: true}
- {key: hicpp-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted, value: true}
- {key: readability-function-cognitive-complexity.IgnoreMacros, value: true}
- {key: readability-function-cognitive-complexity.Threshold, value: 40}
- {key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true}
- {key: hicpp-signed-bitwise.IgnorePositiveIntegerLiterals, value: true}

.clang-format

clang-format可以提供C++的代码格式化,包括很多具体规则的检查,需要通过.clang-format配置文件设置。

可以使用如下的注释去关闭特定位置的格式化

1
2
3
// clang-format off
xxx
// clang-format on

目前使用的 .clang-format 模板如下,更完整的内容可以参考clang官方文档

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
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: true

BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: true

KeepEmptyLinesAtTheStartOfBlocks: false
SeparateDefinitionBlocks: Always
SpacesBeforeTrailingComments: 2
IndentWidth: 4
TabWidth: 4
UseTab: Never
---