Deep dive
Decision table
| Job | Pick | Why |
|---|---|---|
| Library with a stable ABI for other languages | C (or C++ with extern "C" interface) | Every FFI speaks the C ABI |
| Microcontroller with vendor HAL | C (C++ subset if supported) | Vendor toolchains and examples |
| Game engine / real-time | C++20 | Unreal, console SDKs, tooling, talent |
| Numerics / HPC | C++ with Eigen, OpenMP, MPI, or CUDA | Mature, vectorizes well |
| New service in an existing C++ shop | C++20/23 with strict guidelines | Reuse code and skills |
| New security-sensitive component | Rust, called via C ABI | Memory safety by default |
| Build | CMake (≥ 3.20) + Ninja; Bazel for monorepos | Most libraries ship CMake |
| Packages | vcpkg or Conan | Reproducible third-party builds |
Memory and performance model
- C:
malloc/free, pointers, no ownership tracking. C++: RAII (destructors run at scope exit),std::unique_ptr(sole owner, zero overhead),std::shared_ptr(atomic refcount, use sparingly), value semantics and move semantics. - Control over layout: struct packing, alignment (
alignas(64)against false sharing), custom allocators (arenas, slabs), SIMD intrinsics. - Abstractions are zero-cost when inlined: templates monomorphize like Rust generics; virtual calls cost an indirect branch and block inlining.
- UB examples the optimizer exploits: signed integer overflow, out-of-bounds access, use-after-free, data races, strict-aliasing violations, null dereference, uninitialized reads.
Concurrency model
OS threads (std::thread, std::jthread with stop tokens), std::mutex, std::atomic with the C++11 memory model, std::async, parallel algorithms (std::execution::par), C++20 coroutines (language feature; needs a library such as Asio or libunifex/stdexec for scheduling). Data races are UB; ThreadSanitizer finds them.
Code
Modern C++20: RAII for C resources, std::span instead of pointer+length, std::jthread joining automatically.
// g++ -std=c++20 -O2 -Wall -Wextra -pthread checksum.cpp -o checksum
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <numeric>
#include <span>
#include <thread>
#include <vector>
struct FileCloser {
void operator()(std::FILE* f) const noexcept { if (f) std::fclose(f); }
};
using File = std::unique_ptr<std::FILE, FileCloser>; // closed exactly once, even on exceptions
uint64_t fnv1a(std::span<const uint8_t> data) { // pointer + length travel together
uint64_t h = 1469598103934665603ull;
for (uint8_t b : data) { h ^= b; h *= 1099511628211ull; }
return h;
}
int main() {
std::vector<uint8_t> buf(1 << 24);
std::iota(buf.begin(), buf.end(), uint8_t{0}); // wraps mod 256: unsigned, well-defined
const unsigned n = std::max(1u, std::thread::hardware_concurrency());
std::vector<uint64_t> partial(n);
{
std::vector<std::jthread> workers; // destructors join
const size_t chunk = buf.size() / n;
for (unsigned i = 0; i < n; ++i) {
const size_t begin = i * chunk;
const size_t len = (i + 1 == n) ? buf.size() - begin : chunk;
workers.emplace_back([&partial, &buf, i, begin, len] {
// each thread writes only its own slot: no data race
partial[i] = fnv1a(std::span<const uint8_t>(buf).subspan(begin, len));
});
}
}
uint64_t combined = 0;
for (uint64_t p : partial) combined = combined * 31 + p;
File out(std::fopen("checksum.txt", "w"));
if (!out) { std::perror("fopen"); return 1; }
std::fprintf(out.get(), "%016llx\n", static_cast<unsigned long long>(combined));
}
CMake with a sanitizer switch:
cmake_minimum_required(VERSION 3.20)
project(checksum CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(checksum checksum.cpp)
target_compile_options(checksum PRIVATE -Wall -Wextra -Wpedantic -Wconversion)
option(SANITIZE "Build with ASan + UBSan" OFF)
if(SANITIZE)
target_compile_options(checksum PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_options(checksum PRIVATE -fsanitize=address,undefined)
endif()
Release hardening flags (GCC/Clang on Linux, per the OpenSSF guide):
-O2 -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS -fstack-protector-strong \
-fstack-clash-protection -fcf-protection=full -fPIE -pie \
-Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack
Efficiency
- Cost: the cost model is the hardware's: no runtime, no GC, a malloc/free pair per heap object unless you pool. Safety is bought back with tools: AddressSanitizer typically slows a program 2x (Clang docs), ThreadSanitizer about 5x-15x with 5x-10x more memory (Clang docs). Bound: cache misses and memory bandwidth, then branch and call overhead.
- Levers:
- ThinLTO (
-flto=thin) — cross-module inlining with scalable, incremental link times (Clang docs) — large codebases - profile-guided optimization (
-fprofile-instr-generate, IR-based instrumentation for best results) — better inlining and layout from real profiles (Clang docs; gain order of 10 %) — stable workloads - arenas and slabs instead of per-object
new/malloc— fewer allocator calls and cache misses (order of) — many short-lived objects alignas(64)per-thread data — removes false sharing (order of) — contended counters- ccache and Ninja — rebuilds in seconds (order of) — edit-compile loop, CI
- ThinLTO (
- Naive → standard → state of the art:
-O0,mallocper object · slowest, easiest to debug-O2, RAII, containers withreserve· standard release build-O3/-O2with ThinLTO, PGO, arenas and SIMD intrinsics on measured hot loops · the fastest code available; sanitizer builds only in CI
Build and deploy
- CI runs tests under ASan+UBSan and TSan builds, plus fuzzing (libFuzzer, AFL++) on every parser.
- Static analysis: clang-tidy,
-Wall -Wextra -Werroron new code; Clang's-Wunsafe-buffer-usageto find raw pointer arithmetic. - Ship statically linked where licensing allows, or in a container with pinned shared libraries.
- Speed up builds: Ninja, ccache, precompiled headers, C++20 modules where your toolchain supports them.
Pitfalls
- Raw
new/deleteand owning raw pointers; useunique_ptr, containers and values. operator[]without bounds checks on untrusted indices; use.at()or hardened standard library modes in debug and CI.- Iterator and reference invalidation: holding a reference into a
std::vectoracrosspush_back. - Signed overflow assumptions ("it wraps"); it's UB, the optimizer deletes your overflow check.
strcpy,sprintf,getsand manual length arithmetic in C; use bounded functions and track lengths explicitly.- Header-only template libraries blowing up compile times; ODR violations across shared libraries.
std::shared_ptreverywhere: atomic refcount traffic and unclear ownership.- Undefined evaluation order and lifetime of temporaries (dangling
std::string_viewto a temporary string).
Implementations
| name | lang | repo | install | verdict | licence | health | as_of | notes |
|---|---|---|---|---|---|---|---|---|
| LLVM / Clang | C++ | llvm/llvm-project | brew install llvm | — | — | — | — | clang, clang-tidy, sanitizers, lld |
| GCC | C / C++ | gcc-mirror/gcc | brew install gcc | — | — | — | — | GNU compiler collection (read-only mirror) |
| CMake | C++ | Kitware/CMake | brew install cmake | — | — | — | — | de facto build generator |
| Ninja | C++ | ninja-build/ninja | brew install ninja | — | — | — | — | fast build executor |
| ccache | C++ | ccache/ccache | brew install ccache | — | — | — | — | compiler cache |
Sources
- ISO/IEC 9899:2024 (C23) and ISO/IEC 14882:2024 (C++23) standards.
- Stroustrup, Sutter (eds.), C++ Core Guidelines. docs
- OpenSSF, Compiler Options Hardening Guide for C and C++. docs
- Miller, Trends, challenges, and strategic shifts in the software vulnerability mitigation landscape, BlueHat IL 2019 (Microsoft 70 % figure).
- Chromium Project, Memory safety (about 70 % of high-severity bugs). docs
- LLVM project, AddressSanitizer docs, ThreadSanitizer docs, ThinLTO docs.
- Epic Games, Programming with C++ in Unreal Engine. docs · PostgreSQL, About. site · SQLite, Appropriate uses for SQLite. docs