Skip to content

Instantly share code, notes, and snippets.

@GavinRay97
Created January 6, 2023 18:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GavinRay97/f564a9fa00a98183da9ed0166f0a41ed to your computer and use it in GitHub Desktop.
Save GavinRay97/f564a9fa00a98183da9ed0166f0a41ed to your computer and use it in GitHub Desktop.
Modern C++ env setup WIP

Setting up a Modern C++20/23 Environment

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.

Compiler

Below is the compiler configuration we'll use

Common Flags

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_ASSERTIONS enables assertions in the standard library. This is useful for debugging, but should be disabled in production.
  • GLIBCXX_USE_CHAR8_T enables the char8_t type, which is a UTF-8 character type.
  • _USE_GNU enables GNU extensions
  • -Wall -Wextra -Werror enables all warnings, and treats them as errors.
  • -pipe enables piped compilation mode, which is faster than the default mode.
  • -ftrivial-auto-var-init=zeros will zero-initialize all automatic variables, providing default behavior similar to other languages.
  • thread-safety flags enables thread safety analysis in Clang.

Debug Flags

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:

  • DEBUG enables debug mode in the code.
  • GLIBCXX_DEBUG enables debug mode in the standard library.
  • O0 disables optimizations.
  • g enables debug symbols.
  • ggdb3 enables maximum debug information for the GDB debugger.
  • fno-omit-frame-pointer disables frame pointer omission, which is useful for debugging (and required for e.g. ASAN to work)
  • mno-omit-leaf-frame-pointer disables frame pointer omission for leaf functions, which is useful for debugging

Release Flags

{
    "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:

  • NDEBUG disables debug mode in the code.
  • FORTIFY_SOURCE=3 enables additional security checks.
  • O3 enables maximum optimizations (that are still standard-compliant). Ofast is also an option, but is not standard-compliant.
  • march=native enables architecture-specific optimizations.
  • mtune=native enables architecture-specific optimizations.
  • flto enables link-time optimization.

Sanitize Flags

{
    "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_ALLOCATOR enables allocator debugging in the standard library.
  • GLIBCXX_SANITIZE_VECTOR enables vector debugging in the standard library.
  • fsanitize=address,undefined enables address and undefined behavior sanitizers.
  • fsanitize-address-use-after-scope enables use-after-scope detection in the address sanitizer.
  • fsanitize=function,integer,nullability enables function, integer and nullability sanitizers (LLVM only).

Example CMakePresets.json

{
    "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": {}
  }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment