Learn Advanced C++: From Concurrency to Coroutines

Goal: Build the mental models and implementation skills required for modern, high-performance C++. You will understand how C++ handles failure (exceptions + RAII), how to safely share state across threads (synchronization + memory model), how to push correctness into the type system (templates + TMP), and how to structure asynchronous systems with C++20 coroutines. By the end, you will be able to design exception-safe containers, concurrent data structures, compile-time enforced APIs, and coroutine-based network clients with confidence.


Introduction

Advanced C++ is the set of language features and design techniques that turn a correct program into a robust, fast, and maintainable system. It exists to solve the problems you run into when software meets the real world: memory pressure, concurrency, latency, large codebases, and complex invariants.

In this guide you will build:

  • An exception-safe vector (strong guarantee under allocation failure)
  • A thread-safe producer/consumer queue (no busy waiting, clean shutdown)
  • A compile-time unit system (type-level dimension checking)
  • A coroutine-based Redis client (clean async I/O)

Scope: exception safety, RAII, concurrency primitives, the C++ memory model, template metaprogramming, and C++20 coroutines with asynchronous I/O. Out of scope: GUI frameworks, full networking stacks, and OS kernel development.

Big picture (what the topics connect to):

               Failures + Ownership
            (exceptions + RAII + noexcept)
                         |
                         v
          Safe State Under Mutation (Containers)
                         |
                         v
     Shared State (threads, mutexes, condition vars)
                         |
                         v
     Memory Visibility (atomics, happens-before)
                         |
                         v
  Compile-Time Guarantees (templates, TMP, units)
                         |
                         v
  Async Systems (coroutines + event loops + protocols)

How to Use This Guide

  1. Read the theory primer first. It is a mini-book; treat each chapter as a short textbook section.
  2. Pick a project based on your goals (exception safety, concurrency, TMP, or async).
  3. Before coding: answer the design questions and complete the thinking exercise.
  4. Implement in phases, using the hints only if you get stuck.
  5. Validate using the Definition of Done and the testing guidance.
  6. Reflect by answering the interview questions and updating your notes.

Prerequisites & Background

Essential Prerequisites (Must Have)

  • Solid C++ fundamentals: classes, templates, references, RAII basics
  • Memory model basics: stack vs heap, object lifetimes
  • Familiarity with the standard library (std::vector, std::unique_ptr)
  • Basic compilation workflow (CMake or Make)

Helpful But Not Required

  • Move semantics and perfect forwarding
  • Familiarity with std::thread, std::mutex
  • Basic networking (TCP client/server concept)

Self-Assessment Questions

  • Can you explain when copy vs move constructors are called?
  • Can you describe what happens during stack unwinding?
  • What is a data race in C++ and why is it undefined behavior?
  • Why must condition variables always use a predicate?
  • What is SFINAE and how does it prevent compilation errors?
  • What does co_await expand to conceptually?

Development Environment Setup

  • Compiler: GCC 13+ or Clang 16+ with C++20 support
  • Build: CMake 3.20+ or Make
  • Debug: lldb or gdb
  • Sanitizers: ASan/UBSan/TSan (-fsanitize=address,undefined,thread)
  • Redis: Local Redis server (for Project 4)

Time Investment

  • Project 1: 1-2 weeks
  • Project 2: 1-2 weeks
  • Project 3: 2-3 weeks
  • Project 4: 2-4 weeks

Important Reality Check

These projects are intentionally difficult. Expect to read compiler errors carefully, build small experiments, and revisit your design more than once. That is the whole point.


Big Picture / Mental Model

C++ SYSTEMS SAFETY STACK

[Resource Ownership]
        |
        v
[Exception Guarantees] ---> [Container Invariants]
        |
        v
[Mutexes + CondVars] ---> [Safe Shared State]
        |
        v
[Atomics + Memory Order] ---> [Visibility + Ordering]
        |
        v
[Templates + TMP] ---> [Compile-Time Constraints]
        |
        v
[Coroutines + Async I/O] ---> [High-Throughput Services]

Theory Primer (Mini-Book)

Chapter 1: Exception Safety and RAII

Definitions & Key Terms

  • Exception safety: guarantees about program state if an exception is thrown.
  • RAII: bind resource lifetime to object lifetime (acquire in constructor, release in destructor).
  • Stack unwinding: automatic destruction of objects during exception propagation.

Mental Model Diagram (ASCII)

throw --> unwind stack --> destructors run --> invariants restored

[Function A]
  [ResourceGuard g]  <--- destructor runs during unwind
  [Function B]
    throw

How It Works (Step-by-Step)

  1. A function throws an exception.
  2. The runtime begins stack unwinding.
  3. Destructors for in-scope objects run in reverse order.
  4. Resources are released deterministically via RAII.
  5. The exception propagates to the nearest handler.

Minimal Concrete Example

struct FileGuard {
    std::FILE* f;
    explicit FileGuard(const char* path) : f(std::fopen(path, "r")) {
        if (!f) throw std::runtime_error("open failed");
    }
    ~FileGuard() { if (f) std::fclose(f); }
};

Common Misconceptions

  • “RAII means no exceptions” -> RAII exists to make exceptions safe.
  • “Destructors can throw” -> they must be noexcept or you risk terminate.

Check-Your-Understanding Questions

  • Why does RAII make strong exception safety possible?
  • What happens if a destructor throws during stack unwinding?
  • How does RAII differ from finally blocks in other languages?

Where You’ll Apply It

  • Project 1: Exception-Safe Vector
  • Project 2: Thread-Safe Queue (lock guards)

Chapter 2: Exception Guarantees and noexcept

Definitions & Key Terms

  • Basic guarantee: no leaks; object remains valid.
  • Strong guarantee: commit-or-rollback semantics.
  • No-throw guarantee: operation never throws.
  • noexcept: compiler-visible guarantee for optimization and correctness.

Mental Model Diagram (ASCII)

Basic:  State may change, but valid
Strong: Either success OR no change
No-throw: Always success

How It Works (Step-by-Step)

  1. Identify operations that can throw (allocation, copying, user code).
  2. Use temporary objects to build new state.
  3. Commit by swap only after success.
  4. Mark swap/destructors noexcept.

Minimal Concrete Example

T& operator=(T other) noexcept { // copy or move then swap
    swap(other);
    return *this;
}

Common Misconceptions

  • noexcept makes code faster” -> only when it enables move or devirtualization.
  • “Strong guarantee is always possible” -> not if invariant depends on external state.

Check-Your-Understanding Questions

  • Why does std::vector sometimes prefer copy over move?
  • Which operations must be noexcept for strong guarantees?

Where You’ll Apply It

  • Project 1: push_back strong guarantee

Chapter 3: Concurrency Primitives and Coordination

Definitions & Key Terms

  • Data race: two threads access the same location, at least one write, no synchronization.
  • Mutex: mutual exclusion lock to protect shared data.
  • Condition variable: thread waits until a predicate becomes true. It can wake spuriously, so always re-check.

Mental Model Diagram (ASCII)

Producer Thread               Consumer Thread
  lock(m)                       lock(m)
  push(item)                    wait(cv, pred)
  unlock(m)     notify_one ---> unlock(m)

How It Works (Step-by-Step)

  1. Producers lock the mutex, push data, unlock.
  2. Producers notify waiting consumers.
  3. Consumers wait on a condition variable with a predicate.
  4. On wake, consumers re-check the predicate and pop items safely.

Minimal Concrete Example

cv.wait(lock, [&]{ return !q.empty() || shutdown; });

Common Misconceptions

  • “Notify means data is ready” -> spurious wakeups require re-check.
  • “Lock-free means faster” -> often slower if contention is low.

Check-Your-Understanding Questions

  • Why must condition variables use a predicate?
  • What is the difference between notify_one and notify_all?

Where You’ll Apply It

  • Project 2: Thread-Safe Producer/Consumer Queue

Chapter 4: The C++ Memory Model and Atomics

Definitions & Key Terms

  • Happens-before: partial order that defines visibility between operations.
  • Atomic operation: indivisible read/modify/write with ordering semantics.
  • Memory order: relaxed, acquire, release, acq_rel, seq_cst.

Mental Model Diagram (ASCII)

Thread A                       Thread B
store(x=1, release) ---> load(x, acquire)
      (writes before)      (sees writes after)

How It Works (Step-by-Step)

  1. A release store makes prior writes visible.
  2. An acquire load observes those writes.
  3. seq_cst enforces a single global order.

Minimal Concrete Example

std::atomic<int> flag{0};
// writer
flag.store(1, std::memory_order_release);
// reader
if (flag.load(std::memory_order_acquire) == 1) {
    // safe to read data protected by release/acquire
}

Common Misconceptions

  • “Atomic means thread-safe for all data” -> only for that specific variable.
  • memory_order_relaxed is always safe” -> only for counters or independent stats.

Check-Your-Understanding Questions

  • Why are data races undefined behavior in C++?
  • When is seq_cst required vs acquire/release?

Where You’ll Apply It

  • Project 2: shutdown flags, counters
  • Project 4: coroutine scheduling state

Chapter 5: Templates and Compile-Time Programming

Definitions & Key Terms

  • Template instantiation: compiler generates concrete code from templates.
  • SFINAE: substitution failure is not an error.
  • Type traits: compile-time queries about types.
  • std::ratio: compile-time rational numbers.

Mental Model Diagram (ASCII)

Source Template ----> Instantiation ----> Generated Type/Function

How It Works (Step-by-Step)

  1. The compiler substitutes template parameters.
  2. If substitution fails, the candidate is removed (SFINAE).
  3. The best viable overload is chosen.

Minimal Concrete Example

template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
add_one(T v) { return v + 1; }

Common Misconceptions

  • “Templates are just macros” -> templates participate in type checking.
  • “SFINAE hides all errors” -> only during substitution, not in the body.

Check-Your-Understanding Questions

  • What does if constexpr change about template errors?
  • Why is std::ratio useful for unit systems?

Where You’ll Apply It

  • Project 3: Compile-Time Units

Chapter 6: Type-Level Units and Dimensional Analysis

Definitions & Key Terms

  • Dimension vector: type-level exponents for base units.
  • Dimensional analysis: validating physical equations by unit consistency.
  • Zero-cost abstraction: type safety with no runtime overhead.

Mental Model Diagram (ASCII)

Meters:   (L=1, M=0, T=0)
Seconds:  (L=0, M=0, T=1)
Velocity: (L=1, M=0, T=-1)

How It Works (Step-by-Step)

  1. Encode base units as type parameters.
  2. Operator overloads add/subtract exponents at compile time.
  3. Incompatible units fail to compile.

Minimal Concrete Example

template<int M, int L, int T> struct Dim {};
using Meters = Dim<0,1,0>;
using Seconds = Dim<0,0,1>;

Common Misconceptions

  • “This needs runtime checks” -> type-level checks happen at compile time.
  • “Templates always bloat code” -> many get optimized away.

Check-Your-Understanding Questions

  • Why does meters + seconds fail to compile?
  • How do you represent derived units like Newtons?

Where You’ll Apply It

  • Project 3: Compile-Time Units

Chapter 7: Coroutines and Async I/O

Definitions & Key Terms

  • Coroutine: function that can suspend and resume at co_await points.
  • Promise type: the object that controls coroutine behavior.
  • Awaitable: type that defines await_ready, await_suspend, await_resume.
  • Event loop: scheduler that runs pending async operations.

Mental Model Diagram (ASCII)

caller --> coroutine frame --> suspend
           |                (await_suspend)
           v
        resume when I/O completes

How It Works (Step-by-Step)

  1. The compiler transforms a coroutine into a state machine.
  2. co_await calls await_ready/await_suspend.
  3. The coroutine frame is suspended and stored.
  4. The I/O library resumes the coroutine when ready.

Minimal Concrete Example

task<int> fetch() {
    co_await async_wait();
    co_return 42;
}

Common Misconceptions

  • “Coroutines are threads” -> they are stackless state machines.
  • co_await blocks” -> it suspends without blocking the thread.

Check-Your-Understanding Questions

  • Who owns the coroutine frame?
  • What happens if an awaitable resumes immediately?

Where You’ll Apply It

  • Project 4: Coroutine Redis Client

Chapter 8: Redis Protocol (RESP) and Client Design

Definitions & Key Terms

  • RESP: Redis Serialization Protocol (RESP2/RESP3).
  • Bulk string: $<len>\r\n<data>\r\n
  • Array: *<len>\r\n followed by elements.

Mental Model Diagram (ASCII)

Client                Redis
  *2\r\n               (parse array)
  $3\r\nGET\r\n  --->   (execute GET)
  $3\r\nkey\r\n
  <---  $5\r\nvalue\r\n

How It Works (Step-by-Step)

  1. Client encodes commands into RESP arrays.
  2. Server parses the frames and executes commands.
  3. Server replies with a typed RESP response.
  4. Client parses and resumes awaiting coroutine.

Minimal Concrete Example

*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n

Common Misconceptions

  • “Redis is text-only” -> RESP is structured and binary-safe.
  • “Parsing is trivial” -> you must handle partial reads and buffering.

Check-Your-Understanding Questions

  • How do you handle partial frames from a TCP stream?
  • Why is framing important for pipelining?

Where You’ll Apply It

  • Project 4: Coroutine Redis Client

Glossary (High-Signal)

  • RAII: C++ pattern tying resource lifetime to object lifetime.
  • Strong exception guarantee: if an operation fails, state is unchanged.
  • Data race: unsynchronized conflicting access to shared memory.
  • Happens-before: relation that defines visibility between operations.
  • Coroutine frame: compiler-generated storage for coroutine state.
  • RESP: Redis serialization protocol used for commands and replies.

Why Advanced C++ Matters

  • C++ remains a top-ranked systems language in major industry indexes (e.g., TIOBE December 2025 lists C++ in the top 3).
  • Performance-critical systems (databases, trading, game engines) demand deterministic resource control and predictable latency.
  • Modern C++ enables zero-cost abstractions: safety and speed together.

Context & Evolution: C++ evolved from manual error codes and synchronous designs to strong exception safety, fine-grained atomics, and coroutine-based async APIs. This guide focuses on the modern, safe side of that evolution.

Old Style                     Modern C++
-----------                  ----------------------------
manual cleanup               RAII + smart pointers
busy-wait loops              condition variables
runtime unit checks          compile-time unit types
callback pyramids            coroutines

Concept Summary Table

Concept What You Must Internalize Why It Matters Projects
Exception safety basic/strong/no-throw guarantees prevents corruption on failure P01
RAII lifetime ties to resources leak-free cleanup P01, P02
Mutex + condvar safe shared state & coordination avoids data races P02
Memory order visibility + ordering of writes correctness across cores P02, P04
TMP + type traits compile-time constraints safe APIs P03
Dimensional analysis type-safe units prevents logic errors P03
Coroutines suspend/resume async flow clean async I/O P04
RESP protocol request framing, parsing reliable Redis client P04

Project-to-Concept Map

Project Concepts Applied
P01 Exception-Safe Vector RAII, strong guarantee, noexcept
P02 Thread-Safe Queue mutexes, condition variables, atomics, shutdown protocols
P03 Compile-Time Units templates, TMP, std::ratio, static assertions
P04 Coroutine Redis Client coroutines, awaitables, async I/O, RESP parsing

Deep Dive Reading by Concept

Concept Book Chapters / Sections
Exception Safety The C++ Programming Language (Stroustrup) Exceptions and Resource Management chapters
RAII A Tour of C++ (Stroustrup) Resource management + RAII sections
Concurrency The C++ Programming Language (Stroustrup) Concurrency and Memory Model chapters
Templates & TMP The C++ Programming Language (Stroustrup) Templates and Generic Programming chapters
Coroutines A Tour of C++ (Stroustrup) Coroutines overview section
Redis Protocol TCP/IP Illustrated (Stevens) TCP stream behavior + framing concepts

(External references you may also want: Effective C++, C++ Concurrency in Action, C++ Templates: The Complete Guide)


Quick Start (First 48 Hours)

Day 1

  • Read Chapters 1-3 (exceptions + concurrency).
  • Build a minimal RAII guard and a mutex-protected counter.
  • Install Redis locally and verify redis-cli ping.

Day 2

  • Read Chapters 4-8 (memory model, TMP, coroutines, RESP).
  • Build a tiny TMP type trait and compile-time dimension struct.
  • Write a coroutine that suspends and resumes via a timer.

  1. Reliability Path: P01 -> P02
  2. Type-Safety Path: P01 -> P03
  3. Async Systems Path: P02 -> P04
  4. Full Mastery: P01 -> P02 -> P03 -> P04

Success Metrics

  • You can explain exception guarantees with examples.
  • Your concurrent queue never deadlocks and shuts down cleanly.
  • You can intentionally trigger compile-time errors for invalid units.
  • Your coroutine client performs real Redis commands without blocking a thread.

Appendix: Tooling and Debugging Cheatsheet

  • ASan: -fsanitize=address -fno-omit-frame-pointer
  • UBSan: -fsanitize=undefined
  • TSan: -fsanitize=thread
  • Valgrind: valgrind --leak-check=full ./app
  • Thread issues: add logging around lock acquisition order

Projects


Project 1: The Exception-Safe Vector

  • File: LEARN_ADVANCED_CPP_DEEP_DIVE.md
  • Main Programming Language: C++
  • Alternative Programming Languages: Rust (comparison of error handling)
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Exception Safety / RAII
  • Software or Tool: C++17 compiler, ASan/Valgrind
  • Main Book: “Effective C++” by Scott Meyers

What you’ll build: A simplified Vector<T> with strong exception guarantees on push_back and resize.

Why it teaches exception handling: you must guarantee that allocation or element copy failures do not corrupt the container state.

Real World Outcome

A deterministic test harness that injects allocation failures and proves the vector remains valid.

$ ./vector_test --fail-after=5 --seed=42
[TEST] push_back with failure injection
[ALLOC] countdown=5
[ALLOC] countdown=4
[ALLOC] countdown=3
[ALLOC] countdown=2
[ALLOC] countdown=1
[ALLOC] countdown=0 -> throwing std::bad_alloc
[OK] caught std::bad_alloc
[OK] size=5 capacity=8
[OK] elements intact: 0 1 2 3 4
[OK] no leaks detected (ASan)

The Core Question You’re Answering

“How do I guarantee that a container is never corrupted even when constructors or allocations fail?”

Concepts You Must Understand First

  1. RAII and stack unwinding (Stroustrup, C++PL: Exceptions and RAII)
  2. Strong vs basic exception guarantees (Effective C++)
  3. noexcept and move semantics (C++PL: Move semantics)

Questions to Guide Your Design

  • Where can exceptions be thrown inside push_back?
  • How do you keep old state intact while allocating new storage?
  • Which operations must be noexcept for strong guarantees?

Thinking Exercise

Draw the state of a vector before and after a failed reallocation. Which objects are alive? Which destructors must run?

The Interview Questions They’ll Ask

  1. What is the strong exception guarantee?
  2. Why should swap be noexcept?
  3. How does copy-and-swap achieve rollback semantics?
  4. When does std::vector choose copy vs move?

Hints in Layers

Hint 1: Use a temporary buffer

auto newbuf = std::unique_ptr<T[]>(new T[newcap]);

Hint 2: Copy/move into the temporary buffer

for (size_t i = 0; i < size_; ++i) newbuf[i] = data_[i];

Hint 3: Commit with swap

swap(data_, newbuf);

Books That Will Help

| Topic | Book | Chapter | |—|—|—| | Exception safety | Effective C++ | Items on exception safety | | RAII | A Tour of C++ | Resource Management | | Containers | The C++ Programming Language | Containers chapter |

Common Pitfalls & Debugging

Problem: “Vector size changed after bad_alloc”

  • Why: you modified size before successful commit
  • Fix: only update size after allocation + element construction
  • Quick test: run allocation failure harness

Problem: “Double free”

  • Why: ownership not transferred safely
  • Fix: use unique_ptr until commit
  • Quick test: ASan run

Definition of Done

  • push_back provides strong exception guarantee
  • No leaks under ASan/Valgrind
  • swap, destructor are noexcept
  • Allocation failure leaves container valid

Project 2: A Thread-Safe Producer-Consumer Queue

  • File: LEARN_ADVANCED_CPP_DEEP_DIVE.md
  • Main Programming Language: C++
  • Alternative Programming Languages: Java, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Concurrency / Multithreading
  • Software or Tool: std::thread, std::mutex, std::condition_variable
  • Main Book: “C++ Concurrency in Action” by Anthony Williams

What you’ll build: A multi-producer, multi-consumer queue with clean shutdown and zero busy-waiting.

Real World Outcome

A deterministic run with one producer and one consumer (fixed schedule) and a clean shutdown.

$ ./ts_queue_demo --items=5 --seed=7
[PRODUCER] push 0
[CONSUMER] pop 0
[PRODUCER] push 1
[CONSUMER] pop 1
[PRODUCER] push 2
[CONSUMER] pop 2
[PRODUCER] push 3
[CONSUMER] pop 3
[PRODUCER] push 4
[CONSUMER] pop 4
[QUEUE] shutdown
[CONSUMER] exit

The Core Question You’re Answering

“How can multiple threads safely share a queue without races or wasted CPU?”

Concepts You Must Understand First

  1. Mutex + lock guard (C++PL: Concurrency)
  2. Condition variables and spurious wakeups (C++ Concurrency in Action)
  3. Happens-before and data races (C++PL: Memory model)

Questions to Guide Your Design

  • Which operations must hold the lock?
  • What is the predicate for cv.wait?
  • How do you guarantee all consumers exit on shutdown?

Thinking Exercise

Write a timeline of two threads: one producer, one consumer. Show all lock/unlock and notify/wait events.

The Interview Questions They’ll Ask

  1. Why do condition variables require a loop?
  2. What is a data race in C++?
  3. How do you avoid deadlocks in multi-lock code?
  4. What happens if notify occurs before wait?

Hints in Layers

Hint 1: Always wait with a predicate

cv.wait(lock, [&]{ return !q.empty() || shutdown; });

Hint 2: Use notify_all on shutdown

shutdown = true; cv.notify_all();

Hint 3: Return std::optional<T> for pop

if (shutdown && q.empty()) return std::nullopt;

Books That Will Help

| Topic | Book | Chapter | |—|—|—| | Condition variables | C++ Concurrency in Action | Ch. 4 | | Mutexes | The C++ Programming Language | Concurrency chapter | | Thread lifecycle | C++ Concurrency in Action | Ch. 2 |

Common Pitfalls & Debugging

Problem: “Consumer spins at 100% CPU”

  • Why: missing condition variable or predicate
  • Fix: use cv.wait(lock, predicate)
  • Quick test: run idle with no producers

Problem: “Deadlock on shutdown”

  • Why: notify without setting shutdown flag
  • Fix: set flag then notify_all
  • Quick test: run with zero producers

Definition of Done

  • Multiple producers/consumers run without data races
  • Idle consumers do not busy-wait
  • Clean shutdown wakes all consumers
  • TSan reports no races

Project 3: A Compile-Time Unit Conversion Library

  • File: LEARN_ADVANCED_CPP_DEEP_DIVE.md
  • Main Programming Language: C++
  • Alternative Programming Languages: None (C++ specific)
  • Coolness Level: Level 5: Pure Magic
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Template Metaprogramming
  • Software or Tool: C++17 compiler
  • Main Book: “C++ Templates” by Vandevoorde, Josuttis, Gregor

What you’ll build: A zero-runtime-cost unit system that rejects invalid arithmetic at compile time.

Real World Outcome

Successful compile for correct units, and a clear compile error for invalid units.

$ ./units_demo
10 m/s

$ ./build.sh
error: static assertion failed: Cannot add values with different dimensions

The Core Question You’re Answering

“How can the compiler prevent invalid physical equations before the program runs?”

Concepts You Must Understand First

  1. Template parameter packs / type traits (C++ Templates)
  2. std::ratio and compile-time arithmetic (cppreference)
  3. if constexpr / static_assert (C++PL: Templates)

Questions to Guide Your Design

  • How will you encode base dimensions?
  • How will you compute new dimensions for multiply/divide?
  • How will you restrict operator+ to identical dimensions?

Thinking Exercise

Define Force = Mass * Length / Time^2 in terms of dimension exponents. What is the exponent vector?

The Interview Questions They’ll Ask

  1. What is SFINAE and why is it useful?
  2. How does if constexpr change template errors?
  3. Why do unit systems reduce bugs in large codebases?

Hints in Layers

Hint 1: Encode dimensions as an int pack

template<int M, int L, int T> struct Dim {};

Hint 2: Add/subtract dimensions in helper types

template<typename D1, typename D2> struct AddDims;

Hint 3: Use std::is_same_v for operator+

static_assert(std::is_same_v<D1, D2>, "Cannot add...");

Books That Will Help

| Topic | Book | Chapter | |—|—|—| | TMP basics | C++ Templates | Early TMP chapters | | Generic programming | The C++ Programming Language | Templates chapter | | Type systems | A Tour of C++ | Generics section |

Common Pitfalls & Debugging

Problem: “Error messages are unreadable”

  • Why: template errors are verbose
  • Fix: use static_assert with clear messages
  • Quick test: intentionally compile invalid code

Definition of Done

  • Correct units compile and run
  • Invalid unit arithmetic fails to compile with readable error
  • No runtime overhead for unit checks

Project 4: A Coroutine-based Redis Client

  • File: LEARN_ADVANCED_CPP_DEEP_DIVE.md
  • Main Programming Language: C++
  • Alternative Programming Languages: Python, Rust (async/await comparison)
  • Coolness Level: Level 5: Pure Magic
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 5: Master
  • Knowledge Area: Coroutines / Asynchronous Programming / Networking
  • Software or Tool: C++20 compiler, Asio
  • Main Book: “C++20 - The Complete Guide” by Nicolai M. Josuttis

What you’ll build: A coroutine-based Redis client using RESP framing and async I/O. The API should feel synchronous while remaining fully non-blocking.

Real World Outcome

A coroutine client that executes actual Redis commands.

$ ./redis_client_demo
[CONNECT] 127.0.0.1:6379
[CMD] SET user:1 alice
[RESP] +OK
[CMD] GET user:1
[RESP] $5 alice
[CMD] DEL user:1
[RESP] :1

The Core Question You’re Answering

“How do I turn callback-based network I/O into clean, linear code?”

Concepts You Must Understand First

  1. Coroutine promise types and handles (cppreference)
  2. Awaitable interface (cppreference)
  3. RESP framing and parsing (Redis docs)

Questions to Guide Your Design

  • Who owns the coroutine frame?
  • How do you resume the coroutine when data arrives?
  • How do you parse partial RESP frames?

Thinking Exercise

Write a finite state machine for parsing RESP arrays from a TCP stream.

The Interview Questions They’ll Ask

  1. What is a coroutine frame and who destroys it?
  2. How do coroutines differ from threads?
  3. What is the reactor pattern?
  4. How do you avoid blocking in async code?

Hints in Layers

Hint 1: Start with a minimal task<T>

struct task { struct promise_type { /*...*/ }; };

Hint 2: Wrap async ops in awaitables

auto await_suspend(std::coroutine_handle<> h) { async_read(..., [h]{ h.resume(); }); }

Hint 3: Buffer RESP parsing

while (!frame_complete(buf)) co_await read_more();

Books That Will Help

| Topic | Book | Chapter | |—|—|—| | Coroutines | A Tour of C++ | Coroutines section | | Networking | TCP/IP Illustrated | TCP streams, framing | | Async design | The C++ Programming Language | Concurrency chapter |

Common Pitfalls & Debugging

Problem: “Coroutine never resumes”

  • Why: awaitable never calls resume in callback
  • Fix: ensure callback captures and resumes handle
  • Quick test: add logging in await_suspend

Problem: “Protocol parsing fails under load”

  • Why: partial reads not handled
  • Fix: implement incremental parser with buffer
  • Quick test: simulate partial chunks in tests

Definition of Done

  • Client can connect, GET, SET, DEL
  • All I/O is non-blocking
  • Partial RESP frames parsed correctly
  • Clean shutdown and error handling

Summary

Project Main C++ Topic Difficulty Focus
1. Exception-Safe Vector Exception Handling Advanced RAII, exception guarantees, noexcept
2. Thread-Safe Queue Concurrency Advanced std::thread, std::mutex, std::condition_variable
3. Compile-Time Units TMP Expert Type system manipulation, static_assert
4. Coroutine Redis Client Coroutines Master Awaitables, async I/O, RESP parsing