--- name: alexandrescu-modern-cpp-design description: Write C++ code following Andrei Alexandrescu's Modern C++ Design principles. Emphasizes policy-based design, template metaprogramming, and type-safe generic abstractions. Use when designing flexible, reusable libraries or when compile-time computation beats runtime overhead. --- # Andrei Alexandrescu Style Guide ## Overview Andrei Alexandrescu's "Modern C++ Design" revolutionized how we think about C++ templates. His work on Loki library and policy-based design showed that templates are not just for containers—they're a compile-time programming language. ## Core Philosophy > "C++ templates are Turing-complete. Use this power wisely." > "Policy-based design: assemble types from interchangeable parts." Alexandrescu believes in **pushing computation to compile time** and **using the type system as a design tool**, not just a safety mechanism. ## Design Principles 1. **Policy-Based Design**: Build classes from interchangeable policy classes that customize behavior without inheritance overhead. 2. **Compile-Time over Runtime**: What can be computed at compile time should be. 3. **Type Lists and Metaprogramming**: Types themselves become first-class citizens that can be manipulated. 4. **Design Patterns in Types**: Classic GoF patterns implemented with zero runtime overhead. ## When Writing Code ### Always - Consider if behavior can be a compile-time policy - Use `static_assert` to document and enforce requirements - Prefer tag dispatching over runtime branching for type-based logic - Make templates SFINAE-friendly (C++11/14) or use concepts (C++20) - Document template requirements explicitly ### Never - Use runtime polymorphism when static polymorphism suffices - Write duplicate code that differs only in types - Ignore compile-time computation opportunities - Leave template errors to become cryptic instantiation failures ### Prefer - Policy classes over strategy pattern (no vtable) - Type traits over runtime type checking - `constexpr` functions over template metafunctions (modern C++) - Concepts over SFINAE (C++20) - Variadic templates over recursive type lists (modern C++) ## Code Patterns ### Policy-Based Design ```cpp // Traditional OOP: Runtime overhead, fixed at compile time anyway class Widget : public ICreationPolicy, public IThreadingPolicy { /* ... */ }; // Policy-Based: Zero overhead, infinitely configurable template < class CreationPolicy, class ThreadingPolicy = SingleThreaded, class CheckingPolicy = NoChecking > class SmartPtr : public CreationPolicy, public ThreadingPolicy, public CheckingPolicy { // Policies are mixed in, no vtable }; // Usage: Configure at compile time using ThreadSafePtr = SmartPtr; using FastPtr = SmartPtr; // Policies are just classes with required interface struct HeapCreation { template static T* Create() { return new T; } template static void Destroy(T* p) { delete p; } }; struct SingleThreaded { struct Lock { Lock() = default; // No-op }; }; struct MultiThreaded { struct Lock { Lock() { /* acquire mutex */ } ~Lock() { /* release mutex */ } }; }; ``` ### Type Traits and SFINAE ```cpp // Type trait: Does T have a serialize() method? template struct has_serialize : std::false_type {}; template struct has_serialize().serialize())> > : std::true_type {}; // Use it for conditional behavior template auto save(const T& obj) -> std::enable_if_t::value> { obj.serialize(); } template auto save(const T& obj) -> std::enable_if_t::value> { default_serialize(obj); } // C++20: Much cleaner with concepts template concept Serializable = requires(T t) { { t.serialize() } -> std::convertible_to; }; void save(Serializable auto const& obj) { obj.serialize(); } ``` ### Compile-Time Type Lists (Classic Alexandrescu) ```cpp // Type list: A compile-time list of types template struct TypeList {}; // Operations on type lists template struct Length; template struct Length> { static constexpr size_t value = sizeof...(Ts); }; // Get type at index template struct TypeAt; template struct TypeAt<0, TypeList> { using type = Head; }; template struct TypeAt> { using type = typename TypeAt>::type; }; // Usage using MyTypes = TypeList; static_assert(Length::value == 3); using Second = TypeAt<1, MyTypes>::type; // double ``` ### Visitor Pattern via Templates ```cpp // Traditional visitor: Virtual dispatch at every node // Alexandrescu approach: Static visitor with type list template class Variant; template auto visit(Visitor&& v, Variant&& var) { return var.visit(std::forward(v)); } // Modern C++ (std::variant does this) using Value = std::variant; auto result = std::visit(overloaded{ [](int i) { return std::to_string(i); }, [](double d) { return std::to_string(d); }, [](const std::string& s) { return s; } }, value); // The 'overloaded' helper (Alexandrescu-style) template struct overloaded : Ts... { using Ts::operator()...; }; template overloaded(Ts...) -> overloaded; ``` ## Mental Model Alexandrescu thinks of C++ templates as a **compile-time functional language**: 1. **Types as values**: Types can be computed, stored, and transformed 2. **Templates as functions**: Template instantiation is function application 3. **Specialization as pattern matching**: Like case statements on types 4. **Recursion for iteration**: Compile-time loops via recursive templates ## The D Language Connection Alexandrescu later co-designed D, which incorporates many C++ template lessons: - Built-in compile-time function execution - String mixins for code generation - Better error messages for templates These ideas now appear in modern C++ (`constexpr`, `if constexpr`, concepts). ## When to Apply Use Alexandrescu's techniques when: - You need maximum performance (zero runtime overhead) - Behavior variations are known at compile time - You're building a library with many configuration options - Type-based dispatch is frequent Avoid when: - Runtime polymorphism is genuinely needed - Compile times are already problematic - Team isn't comfortable with template metaprogramming