C++ development setup in 2024
By Danny van Kooten on on Permalink.
I've been doing a lot of C and C++ programming lately. After trying a myriad of editors and related tooling, it seems I've finally settled on a satisfactory set-up that is both performant, reliable and powerful.
This post is mostly me praising a set of tools but also showcasing some of their capabilities.
Editor: Sublime Text
The two most popular options right now are probably VSCode and CLion, yet I found neither of them performant or reliable enough for my taste. Instead, I am using good ol' Sublime Text 4 in combination with their Language Server Protocol implementation and Clangd.
Why Sublime, you ask?
- It's an order of magnitude faster than both Clion and VSCode.
- It doesn't suffer from random crashes on large projects.
- It's easy to configure by modifying some JSON config files.
- It's not trying to shove AI down my throat.
- It works without having to spend hours configuring it. Just install LSP and LSP-Clangd through Package Control and you have a powerful editor ready to go.
- It's much more resource efficient than the alternatives. I can have a large C++ project open while it consumes less than 600 MB of RAM and CPU is idling.
The slight bummer is that it's not open source. Still, I really like how a small team from Australia can sit down and build a small but profitable business around a code editor.
The community for plugins is not as vibrant or active as it once was, but for me personally, everything I need is there.
Configuring Clangd
While Clangd works with Sublime Text through LSP-clangd out of the box, we do want to configure a thing or two.
First, enable background indexing explicitly.
- Go to Preferences > Package Settings > LSP > Servers > clangd
- Ensure
initializationOptions["clangd"]["background-index"]
is set totrue
:// Settings in here override those in "LSP-clangd/LSP-clangd.sublime-settings" { "initializationOptions": { "clangd.background-index": true, } }
To let Clangd know how to build your project, there exist several methods:
- A
.clangd
YAML file in your project root containing aCompileFlags
key.CompileFlags: Add: [ -std=c++20, -Wall, -Wextra, -Wpedantic, -Weffc++ ]
- A
compile_flags.txt
file containing one flag per line.-Wall -Wextra
- A
compile_commands.json
. You can have this file generated by CMake by setting theCMAKE_EXPORT_COMPILE_COMMANDS
environment variable.
Compiler (flags)
As my workstation is running Debian, I tend to compile the C and C++ projects that I work on using whatever version of GCC and Clang is in the official package repositories.
Currently, this means gcc 12 and clang 14, both of which have near complete support for -std=c++20
1 2.
When a newer compiler is needed, there's always building GCC from source or LLVM's APT repositories.
Both GCC and Clang are conservative with warnings, so we should enable (some of) them explicitly. The group of warnings from -Wall
and -Wextra
is what I always enable as a baseline 3 4.
In terms of compilation profiles, I tend to use just three:
Development
Development mode cares mostly about fast compilation times. The LDFLAGS
environment value is set to instruct our compiler to use mold for the linking stage, which is usually an order of magnitude faster than ld.
CFLAGS="-std=c11 -Wall -Wextra -Wvla -Wformat -Wformat=2 -Wconversion"
CXXFLAGS="-std=c++20 -Wall -Wextra"
LDFLAGS="-fuse-ld=mold"
Debug
In debug mode we want debug symbols, stack traces and runtime checks from both Address Sanitizer and Undefined Behavior Sanitizer.
CFLAGS="-g -fsanitize=address,undefined -fno-omit-frame-pointer"
CXXFLAGS="-g -fsanitize=address,undefined -D_GLIBCXX_ASSERTIONS -fno-omit-frame-pointer"
ASAN_OPTIONS="strict_string_checks=1:strict_memcmp=1:quarantine_size_mb=512:detect_stack_use_after_return=1:check_initialization_order=1"
UBSAN_OPTIONS="print_stacktrace=1"
-D_GLIBCXX_ASSERTIONS
adds run-time bound checks for C++ containers from the STL. There is also -D_FORTIFY_SOURCE=2
which adds run-time buffer overflow detection for a collection of functions from the glibc, but it requires an optimization level of 1 or higher (-O1
).
ASAN_OPTIONS
is used to configure Address Sanitizer to allow it to use more memory for detecting use-after-free errors and perform some of its stricter checks which are disabled by default.
UBSAN_OPTIONS
is used alongside -fno-omit-frame-pointer
to configure Undefined Behavior Sanitizer to print a symbolized strack trace for each error report.
Release
In release mode we want the compiler to produce the fastest code possible at the cost of longer compilation times.
CFLAGS="-O3"
CXXFLAGS="-O3"
If you don't care about portability and want to target your specific CPU, you could add -march=native
and -mtune=native
.
Diagnostics: clang-tidy
Since we are using Clangd as our Language Server, we can instruct it to emit all sorts of diagnostics (besides just compiler warnings) through clang-tidy.
clang-tidy is disabled by default, but we can enable it by modifying the settings for the LSP-clangd plugin.
- Go to Preferences > Package Settings > LSP > Servers > Clangd.
- Ensure
initialiationOptions.clangd["clang-tidy"]
is set totrue
:// Settings in here override those in "LSP-clangd/LSP-clangd.sublime-settings" { "initializationOptions": { "clangd.clang-tidy": true, "clangd.background-index": true, "clangd.header-insertion": "iwyu", "clangd.completion-style": "detailed", } }
- You can configure clang-tidy by creating a
.clangd
YAML file In your project root. In it, you can add or remove the clang-tidy checks you want. I mostly stick to the ones fromperformance-*
andcppcoreguidelines-*
.Diagnostics: ClangTidy: Add: - performance-* - cppcoreguidelines-* Remove: - cppcoreguidelines-avoid-magic-numbers
The LLVM suite also contains a run-clang-tidy
command which you can use to run a single check against your source directory:
run-clang-tidy -checks='-*,performance-unnecessary-value-param' src/
Formatter: clang-format
We can use clang-format through Sublime's LSP-clangd plugin.
Go to Preferences > Package Settings > LSP > Settings and ensure lsp_format_on_save
is set to true
.
Your code style can be configured through a .clang-format
YAML file in your project root.
Profiling: perf
To find performance bottlenecks, I've not come across anything that beats perf + flamegraph.pl.
The author of the latter tool has a great post on his blog with many language specific tips on how to best create flamegraphs from a perf report.
Comments are welcome. You can email me at hi @ this domain.