home / skills / copyleftdev / sk1llz / alexandrescu

alexandrescu skill

/languages/cpp/alexandrescu

This skill helps you apply Andrei Alexandrescu's Modern C++ Design, promoting policy-based templates and compile-time abstractions for zero-runtime overhead.

npx playbooks add skill copyleftdev/sk1llz --skill alexandrescu

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.7 KB
---
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<HeapCreation, MultiThreaded, AssertCheck>;
using FastPtr = SmartPtr<HeapCreation, SingleThreaded, NoChecking>;

// Policies are just classes with required interface
struct HeapCreation {
    template<class T>
    static T* Create() { return new T; }
    
    template<class T>
    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<typename T, typename = void>
struct has_serialize : std::false_type {};

template<typename T>
struct has_serialize<T, 
    std::void_t<decltype(std::declval<T>().serialize())>
> : std::true_type {};

// Use it for conditional behavior
template<typename T>
auto save(const T& obj) -> std::enable_if_t<has_serialize<T>::value> {
    obj.serialize();
}

template<typename T>
auto save(const T& obj) -> std::enable_if_t<!has_serialize<T>::value> {
    default_serialize(obj);
}

// C++20: Much cleaner with concepts
template<typename T>
concept Serializable = requires(T t) {
    { t.serialize() } -> std::convertible_to<std::string>;
};

void save(Serializable auto const& obj) {
    obj.serialize();
}
```

### Compile-Time Type Lists (Classic Alexandrescu)

```cpp
// Type list: A compile-time list of types
template<typename... Ts>
struct TypeList {};

// Operations on type lists
template<typename List>
struct Length;

template<typename... Ts>
struct Length<TypeList<Ts...>> {
    static constexpr size_t value = sizeof...(Ts);
};

// Get type at index
template<size_t I, typename List>
struct TypeAt;

template<typename Head, typename... Tail>
struct TypeAt<0, TypeList<Head, Tail...>> {
    using type = Head;
};

template<size_t I, typename Head, typename... Tail>
struct TypeAt<I, TypeList<Head, Tail...>> {
    using type = typename TypeAt<I - 1, TypeList<Tail...>>::type;
};

// Usage
using MyTypes = TypeList<int, double, std::string>;
static_assert(Length<MyTypes>::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<typename... Types>
class Variant;

template<typename Visitor, typename Variant>
auto visit(Visitor&& v, Variant&& var) {
    return var.visit(std::forward<Visitor>(v));
}

// Modern C++ (std::variant does this)
using Value = std::variant<int, double, std::string>;

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<class... Ts> 
struct overloaded : Ts... { 
    using Ts::operator()...; 
};
template<class... Ts> 
overloaded(Ts...) -> overloaded<Ts...>;
```

## 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

Overview

This skill guides writing C++ code following Andrei Alexandrescu's Modern C++ Design principles, emphasizing policy-based design, template metaprogramming, and type-safe generic abstractions. It helps you design zero-overhead, highly configurable libraries where compile-time computation replaces runtime cost. Use it to make reusable, expressive, and efficient APIs that leverage the type system as a design tool.

How this skill works

The skill inspects design intent and suggests transforming runtime variability into compile-time policies and type-level computations. It recommends patterns like policy mixins, type lists, SFINAE-to-concepts migration, constexpr replacements for metafunctions, and static visitor/variant techniques. It also provides concrete idioms for enforcing template requirements, tagging dispatch, and safe compile-time checks.

When to use it

  • Designing libraries that must be highly configurable with no vtable overhead
  • When behavior variations are known at compile time and performance matters
  • Replacing runtime branching with type-based dispatch or constexpr evaluation
  • Building type-safe DSLs, compile-time registries, or heterogenous containers
  • When you need clear, documentable template requirements and improved compile-time diagnostics

Best practices

  • Prefer policy classes to runtime inheritance; mix policies by composition to avoid vtables
  • Use static_assert and concepts to document and enforce template contracts
  • Favor constexpr and consteval for computation where possible; use template metaprogramming only when necessary
  • Make templates SFINAE-friendly or migrate to C++20 concepts for clearer overloads
  • Use variadic templates and fold expressions instead of recursive type lists when available
  • Measure compile-time cost; avoid unnecessary metaprogramming that slows builds without runtime benefit

Example use cases

  • Configurable smart pointers built from creation, threading, and checking policy classes
  • Compile-time registries and dispatch tables implemented with type lists or std::variant
  • Conditional serialization that detects serialize() at compile time using traits or concepts
  • Zero-overhead design patterns: static visitors, policy-based factories, and mixin composition
  • Library APIs exposing customizable behavior via template parameters rather than runtime flags

FAQ

When should I not use Alexandrescu-style templates?

Avoid heavy template techniques when runtime polymorphism is required, when compile times are already critical, or when the team lacks template expertise.

Should I prefer concepts or SFINAE?

Use concepts (C++20) for clearer, faster-to-diagnose constraints. Use SFINAE only for older standards or specific backward-compatible overloads.