This document describes configuring a development environment for modern C++. Some of the flags and warnings in this document are not common knowledge, or found in the documentation of the respective toolchain.
Below is the compiler configuration we'll use
We can start by definining a set of common DEFINEs and compiler flags for C/C++.
{
"COMMON_DEFINES": "-D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CHAR8_T -D__USE_GNU",
"COMMON_C_FLAGS": " $env{COMMON_DEFINES} -Wall -Wextra -Werror -pipe -ftrivial-auto-var-init=zero",
"COMMON_CXX_FLAGS": "$env{COMMON_C_FLAGS}",
"CLANG_CXX_FLAGS": "-Wthread-safety -Wthread-safety-beta -Wthread-safety-verbose"
}Explaining the above:
GLIBCXX_ASSERTIONSenables assertions in the standard library. This is useful for debugging, but should be disabled in production.GLIBCXX_USE_CHAR8_Tenables thechar8_ttype, which is a UTF-8 character type._USE_GNUenables GNU extensions-Wall -Wextra -Werrorenables all warnings, and treats them as errors.-pipeenables piped compilation mode, which is faster than the default mode.-ftrivial-auto-var-init=zeroswill zero-initialize all automatic variables, providing default behavior similar to other languages.thread-safetyflags enables thread safety analysis in Clang.
All following configurations will build on top of the COMMON flags and configuration.
{
"DEBUG_DEFINES": "-DDEBUG -D_GLIBCXX_DEBUG",
"DEBUG_C_FLAGS": "$env{DEBUG_DEFINES} -O0 -g -ggdb3 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer",
"DEBUG_CXX_FLAGS": "$env{DEBUG_C_FLAGS}"
}Exaplaining the above:
DEBUGenables debug mode in the code.GLIBCXX_DEBUGenables debug mode in the standard library.O0disables optimizations.genables debug symbols.ggdb3enables maximum debug information for the GDB debugger.fno-omit-frame-pointerdisables frame pointer omission, which is useful for debugging (and required for e.g. ASAN to work)mno-omit-leaf-frame-pointerdisables frame pointer omission for leaf functions, which is useful for debugging
{
"RELEASE_DEFINES": "-DNDEBUG -D_FORTIFY_SOURCE=3",
"RELEASE_C_FLAGS": "$env{RELEASE_DEFINES} -O3 -march=native -mtune=native -flto",
"RELEASE_CXX_FLAGS": "$env{RELEASE_C_FLAGS}"
}Explaining the above:
NDEBUGdisables debug mode in the code.FORTIFY_SOURCE=3enables additional security checks.O3enables maximum optimizations (that are still standard-compliant).Ofastis also an option, but is not standard-compliant.march=nativeenables architecture-specific optimizations.mtune=nativeenables architecture-specific optimizations.fltoenables link-time optimization.
{
"COMMON_SANITIZE_DEFINES": "-D_GLIBCXX_SANITIZE_STD_ALLOCATOR -D_GLIBCXX_SANITIZE_VECTOR",
"COMMON_SANITIZER_FLAGS": "$env{COMMON_SANITIZE_DEFINES} -fsanitize=address,undefined -fsanitize-address-use-after-scope",
"GCC_SANITIZER_FLAGS": "$env{COMMON_SANITIZER_FLAGS}",
"LLVM_SANITIZER_FLAGS": "$env{COMMON_SANITIZER_FLAGS} -fsanitize=function,integer,nullability"
}Explaining the above:
GLIBCXX_SANITIZE_STD_ALLOCATORenables allocator debugging in the standard library.GLIBCXX_SANITIZE_VECTORenables vector debugging in the standard library.fsanitize=address,undefinedenables address and undefined behavior sanitizers.fsanitize-address-use-after-scopeenables use-after-scope detection in the address sanitizer.fsanitize=function,integer,nullabilityenables function, integer and nullability sanitizers (LLVM only).
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 23,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default Config",
"description": "Default build using Ninja generator",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/default",
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"CMAKE_COLOR_DIAGNOSTICS": "ON",
"CMAKE_C_STANDARD": "23",
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_EXTENSIONS": "ON",
"CMAKE_CXX_STANDARD_REQUIRED": "OFF",
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
"CMAKE_C_COMPILER_LAUNCHER": "ccache",
"CMAKE_LINK_WHAT_YOU_USE": "ON"
},
"environment": {
"COMMON_DEFINES": "-D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CHAR8_T -D__USE_GNU",
"COMMON_C_FLAGS": " $env{COMMON_DEFINES} -Wall -Wextra -Werror -Wno-unused-variable -pipe -ftrivial-auto-var-init=zero",
"COMMON_CXX_FLAGS": "$env{COMMON_C_FLAGS}",
"DEBUG_DEFINES": "-DDEBUG -D_GLIBCXX_DEBUG",
"DEBUG_C_FLAGS": "$env{DEBUG_DEFINES} -O0 -g -ggdb3 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer",
"DEBUG_CXX_FLAGS": "$env{DEBUG_C_FLAGS}",
"RELEASE_DEFINES": "-D_FORTIFY_SOURCE=3",
"RELEASE_C_FLAGS": "$env{RELEASE_DEFINES} -Ofast -march=native -mtune=native -flto",
"RELEASE_CXX_FLAGS": "$env{RELEASE_C_FLAGS}",
"COMMON_SANITIZER_DEFINES": "-D_GLIBCXX_SANITIZE_STD_ALLOCATOR -D_GLIBCXX_SANITIZE_VECTOR",
"COMMON_SANITIZER_FLAGS": "$env{COMMON_SANITIZER_DEFINES} -fsanitize=address,undefined -fsanitize-address-use-after-scope",
"GCC_SANITIZER_FLAGS": "$env{COMMON_SANITIZER_FLAGS}",
"LLVM_SANITIZER_FLAGS": "$env{COMMON_SANITIZER_FLAGS} -fsanitize=function,integer,nullability",
"CLANG_ONLY_C_FLAGS": "",
"CLANG_ONLY_CXX_FLAGS": "$env{CLANG_ONLY_C_FLAGS} -Wthread-safety -Wthread-safety-beta -Wthread-safety-verbose"
},
"vendor": {}
},
{
"name": "debug",
"displayName": "Debug Build",
"description": "Debug build using Ninja generator",
"generator": "Ninja",
"inherits": "default",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_C_FLAGS": "$env{COMMON_C_FLAGS} $env{DEBUG_C_FLAGS}",
"CMAKE_CXX_FLAGS": "$env{COMMON_CXX_FLAGS} $env{DEBUG_CXX_FLAGS}"
}
},
{
"name": "debug-gcc-fanalyzer",
"displayName": "Debug GCC with -fanalyzer",
"description": "Debug build with GCC and -fanalyzer",
"inherits": "debug",
"cacheVariables": {
"CMAKE_C_COMPILER": "gcc",
"CMAKE_CXX_COMPILER": "g++",
"CMAKE_C_FLAGS": "$env{COMMON_C_FLAGS} $env{DEBUG_C_FLAGS} $env{COMMON_SANITIZER_FLAGS} -fanalyzer",
"CMAKE_CXX_FLAGS": "$env{COMMON_CXX_FLAGS} $env{DEBUG_CXX_FLAGS} $env{COMMON_SANITIZER_FLAGS} -fanalyzer"
}
},
{
"name": "release",
"displayName": "Release Build",
"description": "Release build using Ninja generator",
"inherits": "default",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_C_FLAGS": "$env{COMMON_C_FLAGS} $env{RELEASE_C_FLAGS}",
"CMAKE_CXX_FLAGS": "$env{COMMON_CXX_FLAGS} $env{RELEASE_CXX_FLAGS}"
}
},
{
"name": "sanitize",
"displayName": "Sanitize Build",
"description": "Sanitize build using Ninja generator",
"inherits": "debug",
"binaryDir": "${sourceDir}/build/sanitize",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang",
"CMAKE_CXX_COMPILER": "clang++",
"CMAKE_C_FLAGS": "$env{COMMON_C_FLAGS} $env{DEBUG_C_FLAGS} $env{LLVM_SANITIZER_FLAGS} $env{CLANG_ONLY_C_FLAGS}",
"CMAKE_CXX_FLAGS": "$env{COMMON_CXX_FLAGS} $env{DEBUG_CXX_FLAGS} $env{LLVM_SANITIZER_FLAGS} $env{CLANG_ONLY_CXX_FLAGS}",
"CMAKE_EXE_LINKER_FLAGS": "$env{LLVM_SANITIZER_FLAGS} -fuse-ld=lld",
"CMAKE_SHARED_LINKER_FLAGS": "$env{LLVM_SANITIZER_FLAGS} -fuse-ld=lld"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default"
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "default",
"output": {
"outputOnFailure": true
},
"execution": {
"noTestsAction": "error",
"stopOnFailure": true
}
}
],
"vendor": {}
}