C++ that survives contact with a deadline

Why it is slow, why it crashed, why the build takes nine minutes.

A course for programmers who can already ship code and whose C++ compiles but cannot be explained. It works through memory, lifetime, the parts of the standard library worth knowing, the build, and the tooling that turns a vague crash into a reproducible one. The last module is real-time safety, the rules a C++ audio engine has to hold to before anyone hears it.

Level: Intermediate · Hours: 9 · Price: £59

What you build: You build a small block-based audio processor with a test suite that fails the build when the processing path allocates, locks, or does unbounded work.

What you will be able to do

  • Say where any object in your program lives and when it dies
  • Read a profile and a disassembly well enough to explain why a function is slow
  • Turn an intermittent crash into a reproducible one with sanitizers and a minimal case
  • Pick a container and a string type from the cost, rather than from habit
  • Write a processing function that allocates nothing and blocks on nothing, and prove it in a test

What you need first

  • You can already program in some language and have written loops, functions and types
  • You have compiled a C++ program at least once, even if you copied the build file
  • You are comfortable on a command line

The syllabus

  • Memory and lifetime: What the machine actually does with your object. You can point at any object in a function and say where it lives, when it dies, and how many trips to the allocator it costs. (free to read)
  • Memory and lifetime: Who owns this pointer. You can read any function signature and say who is responsible for freeing what, and express that answer in the type using unique_ptr, shared_ptr or a plain reference.
  • Memory and lifetime: Copies, moves, and the rule of zero. You can explain what std::move does and does not do, tell a copy from a move in a stack trace, and write types that need no destructor, copy constructor or assignment operator at all.
  • Types and interfaces: What const actually promises. You can say what const stops the compiler doing, what it does not stop anyone doing, and why const on a member function is a claim about threads as much as about mutation.
  • Types and interfaces: A type that is hard to misuse. You can turn a struct with an unwritten set of rules into a type whose invalid states will not compile, using constructors, access control and strong typedefs.
  • Types and interfaces: Templates and the errors they produce. You can write a function template that works on several sample types, constrain it so a wrong call fails at the call site, and read a 200 line template error down to the one line that matters.
  • The standard library worth knowing: vector, reserve, and the growth you did not budget for. You can predict when a vector reallocates, name the references it invalidates when it does, size one so that never happens inside a deadline, and pass the buffer on as a span instead of a pointer and a length that can disagree.
  • The standard library worth knowing: Choosing a container from its cost. You can pick between vector, map, unordered_map and a flat sorted array by the cost of the operation you actually do most, and explain why the linear scan usually wins below a few hundred elements.
  • The standard library worth knowing: Strings without the copies. You can move text through a program with string_view and a single owning string, and know exactly which of your string operations still allocates.
  • Builds and tooling: Why the build takes nine minutes. You can measure where compile time goes, cut it with forward declarations, the pimpl idiom and precompiled headers, and say what an incremental build should cost.
  • Builds and tooling: CMake you can read six months later. You can write a CMakeLists that describes targets and their public and private requirements, so that a new dependency is three lines rather than a day.
  • Builds and tooling: Turning a crash into a reproducible case. You can take an intermittent crash and produce a deterministic failing test, using the address and thread sanitizers to name the use after free or the race, a debugger to read the frame, and a systematic cut down of the input.
  • Builds and tooling: Measure before you optimise. You can profile a running program, read the result well enough to name the function that costs the most, and write a microbenchmark that the optimiser cannot delete.
  • Real-time safety: The audio callback contract. You can list what a processing function is forbidden to do, calculate the deadline from the sample rate and block size, and recognise a violation in unfamiliar code.
  • Real-time safety: Getting data across without a lock. You can move parameters, buffers and messages between a normal thread and the audio thread with atomics and a single producer single consumer queue, and say why a mutex there is a design error.
  • Real-time safety: Proving it in the test suite. You can build a harness that fails the build when the processing path allocates, locks or runs longer than its budget, which is the harness the plugin course starts from.

All courses