Project 12: Template Metaprogramming Library

Build a compile-time computation library with type lists, traits, and zero-runtime-cost abstractions

Quick Reference

Attribute Value
Difficulty Master
Time Estimate 1-2 months
Language C++ (C++17/20/23)
Prerequisites All previous projects, template basics
Key Topics Variadic templates, SFINAE, concepts, constexpr, type lists, TMP

1. Learning Objectives

By completing this project, you will:

  1. Master variadic templates - Work with parameter packs, fold expressions, and recursive instantiation
  2. Understand SFINAE deeply - Control overload resolution through substitution failure
  3. Use C++20 concepts - Write cleaner template constraints
  4. Implement type lists - Store, query, and transform types at compile time
  5. Build compile-time strings - Manipulate strings as template parameters
  6. Create zero-cost abstractions - Everything computed at compile time, nothing at runtime
  7. Understand type traits - Query and transform type properties
  8. Apply constexpr/consteval - Force compile-time evaluation

2. Theoretical Foundation

2.1 Core Concepts

Template metaprogramming (TMP) is programming where the compiler is the runtime. Instead of computing values when your program runs, TMP computes types and values when your program compiles. The result: zero runtime cost.

The Metaprogramming Paradigm:

Traditional Programming               Template Metaprogramming
┌─────────────────────────┐          ┌─────────────────────────┐
│ Runtime                 │          │ Compile Time            │
│                         │          │                         │
│ • Variables hold values │          │ • Types ARE values      │
│ • Functions transform   │          │ • Templates ARE funcs   │
│   values                │          │ • Instantiation IS      │
│ • Loops iterate         │          │   evaluation            │
│ • Conditions branch     │          │ • Specialization IS     │
│                         │          │   pattern matching      │
│ • Computed when running │          │ • Computed when         │
│                         │          │   compiling             │
└─────────────────────────┘          └─────────────────────────┘

Key Insight: Templates are a Turing-complete functional language embedded in C++. You can compute anything at compile time that you could compute at runtime.

2.2 Why This Matters

Template metaprogramming enables:

  1. Zero-cost abstractions - High-level code that compiles to optimal machine code
  2. Type-safe APIs - Catch errors at compile time, not runtime
  3. Performance-critical libraries - Eigen, Boost, STL all use TMP heavily
  4. Domain-specific languages - Express patterns in types (Spirit, Hana)
  5. Static reflection - Query types at compile time before C++26 reflection

Real-world usage:

  • std::tuple, std::variant, std::optional - Type manipulation
  • Eigen matrix library - Expression templates for optimal linear algebra
  • Boost.Spirit - Parser combinators expressed in types
  • Boost.Hana - Metaprogramming with value semantics
  • JSON libraries - Compile-time JSON parsing (simdjson concepts)

2.3 Historical Context

1990: Templates introduced in C++
      └── Just for generic containers

1994: Erwin Unruh's prime number program
      └── First demonstration: compute primes during compilation
      └── Compiler error messages contained the primes!

1998: STL standardized
      └── Heavy use of template techniques

2003: Alexandrescu's "Modern C++ Design"
      └── Formalized TMP as a paradigm
      └── Policy-based design, type lists, traits

2011: C++11 - variadic templates, constexpr
      └── Much cleaner TMP syntax

2014-20: constexpr expansion, concepts
      └── constexpr if, constexpr virtual, concepts
      └── TMP becomes mainstream

2023: Deducing this, more constexpr
      └── Easier recursive lambdas
      └── constexpr containers

Future: Static reflection (C++26?)
      └── Will replace much of TMP

2.4 Common Misconceptions

  1. “TMP is just for library authors”
    • Reality: Understanding TMP helps you use STL/Boost effectively and debug template errors
  2. “TMP makes code slower”
    • Reality: TMP has zero runtime cost—it moves computation to compile time
  3. “TMP is obsolete with constexpr”
    • Reality: constexpr computes values; TMP manipulates types. Both are needed
  4. “TMP produces unreadable code”
    • Reality: Modern C++ (concepts, if constexpr) makes TMP much cleaner
  5. “Template errors are impossible to read”
    • Reality: Concepts produce clear error messages; understanding TMP helps debug the rest

3. Project Specification

3.1 What You Will Build

A template metaprogramming library that provides:

  1. Type Lists - TypeList<int, double, string> with operations like At, Length, Contains, Filter, Transform
  2. Type Traits - Extended traits beyond <type_traits>
  3. Compile-Time Strings - FixedString that can be template parameters
  4. Compile-Time JSON Parser - Parse JSON literals at compile time
  5. Type-Safe Printf - Format strings checked at compile time
  6. Value Lists - ValueList<1, 2, 3> with arithmetic operations

3.2 Functional Requirements

Type List Operations:

  • Length<List> - Number of types in list
  • At<List, Index> - Type at index
  • Contains<List, T> - Check if T is in list
  • IndexOf<List, T> - Find index of T
  • PushFront<List, T> / PushBack<List, T> - Add types
  • PopFront<List> / PopBack<List> - Remove types
  • Concat<List1, List2> - Concatenate lists
  • Filter<List, Predicate> - Keep types matching predicate
  • Transform<List, MetaFunction> - Apply function to each type
  • Unique<List> - Remove duplicate types
  • Reverse<List> - Reverse type order

Compile-Time Strings:

  • Construction from string literals
  • Concatenation
  • Substring extraction
  • Comparison
  • Character iteration (for compile-time parsing)

Compile-Time JSON:

  • Parse JSON literals
  • Access object properties
  • Access array elements
  • Type-safe value extraction

3.3 Non-Functional Requirements

  • Compilation: Must work with C++17 (some features C++20)
  • Zero Runtime Cost: All computation at compile time
  • Clear Error Messages: Use concepts/static_assert for helpful errors
  • Header-Only: No runtime library needed
  • Standards Compliant: No compiler extensions

3.4 Example Usage / Output

// Type list operations
using MyTypes = TypeList<int, double, std::string>;
static_assert(Length<MyTypes>::value == 3);
static_assert(std::is_same_v<At<MyTypes, 0>, int>);
static_assert(Contains<MyTypes, double>::value);
static_assert(IndexOf<MyTypes, std::string>::value == 2);

using Filtered = Filter<MyTypes, std::is_arithmetic>;  // TypeList<int, double>
using Transformed = Transform<MyTypes, std::add_pointer>;  // TypeList<int*, double*, std::string*>

// Compile-time string
constexpr FixedString hello = "Hello";
constexpr FixedString world = "World";
constexpr auto greeting = hello + ", " + world + "!";
static_assert(greeting == "Hello, World!");

// Compile-time JSON parsing
constexpr auto json = R"({"name": "Alice", "age": 30})"_json;
static_assert(json["name"] == "Alice");
static_assert(json["age"] == 30);

// Type-safe printf format checking
print<"Hello, %s! You are %d years old.">("Alice", 30);  // OK
// print<"Hello, %s!">(42);  // Compile error: expected string, got int

// Reflection-like serialization
struct Person {
    std::string name;
    int age;
    REFLECT(name, age)
};

constexpr auto json_str = to_json(Person{"Bob", 25});
// json_str is computed at compile time!

3.5 Real World Outcome

// After implementing your library, this code compiles:

#include "meta/typelist.hpp"
#include "meta/fixed_string.hpp"
#include "meta/compile_time_json.hpp"

// 1. Type list demonstration
using Numbers = TypeList<int, long, float, double>;
using Strings = TypeList<std::string, std::string_view, const char*>;

// Filter to only integral types
using Integrals = Filter<Numbers, std::is_integral>;
static_assert(std::is_same_v<Integrals, TypeList<int, long>>);

// Transform to add const
using ConstNumbers = Transform<Numbers, std::add_const>;
static_assert(std::is_same_v<At<ConstNumbers, 0>, const int>);

// Concatenate and remove duplicates
using All = Concat<Numbers, TypeList<int, char>>;
using UniqueAll = Unique<All>;

// 2. Compile-time string manipulation
constexpr FixedString path = "/api/v1/users";
constexpr auto segments = split<path, '/'>();  // ["", "api", "v1", "users"]
static_assert(segments[1] == "api");

// Format strings with compile-time validation
constexpr auto fmt = FixedString{"User: %s (age %d)"};
auto message = format<fmt>("Alice", 30);  // OK
// auto bad = format<fmt>(42, "Alice");  // ERROR: wrong types

// 3. Compile-time JSON
constexpr auto config = R"({
    "server": {
        "host": "localhost",
        "port": 8080
    },
    "features": ["logging", "metrics"]
})"_json;

static_assert(config["server"]["host"] == "localhost");
static_assert(config["server"]["port"] == 8080);
static_assert(config["features"][0] == "logging");

// 4. Type-indexed tuple variant
using Variant = TypeMap<
    Pair<int, std::string>,
    Pair<double, std::vector<int>>,
    Pair<char, std::array<int, 10>>
>;

static_assert(std::is_same_v<Get<Variant, int>, std::string>);

// 5. Compile-time reflection helper
struct Point {
    double x, y, z;
    META_REFLECT(x, y, z)
};

static_assert(field_count<Point> == 3);
static_assert(field_name<Point, 0> == "x");

// All of the above: ZERO runtime cost
int main() {
    std::cout << "All compile-time assertions passed!" << std::endl;
    return 0;
}

Compilation output (errors when misused):

$ g++ -std=c++20 example.cpp

# If you make a mistake like:
# format<"Hello %s">(42)

example.cpp:45:12: error: static assertion failed
   45 |     static_assert(is_same_v<ArgType, const char*>,
      |            ^~~~~~~~~~~~~
note: Format specifier '%s' requires string type, but got 'int'

4. Solution Architecture

4.1 High-Level Design

Template Metaprogramming Library Architecture
═══════════════════════════════════════════════════════════════════

Layer 4: User-Facing APIs
┌─────────────────────────────────────────────────────────────────┐
│  format<"...">()     json["key"]      TypeList<Ts...>          │
│  REFLECT(...)        for_each_type    static_assert checks     │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
Layer 3: High-Level Metafunctions
┌─────────────────────────────────────────────────────────────────┐
│  Filter<List, Pred>    Transform<List, F>    Unique<List>      │
│  Split<String>         ParseJSON<String>     FormatCheck<...>  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
Layer 2: Core Metafunctions
┌─────────────────────────────────────────────────────────────────┐
│  At<List, I>           PushBack<List, T>     Concat<L1, L2>    │
│  Length<List>          Contains<List, T>     IndexOf<List, T>  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
Layer 1: Primitives
┌─────────────────────────────────────────────────────────────────┐
│  TypeList<Ts...>       FixedString<N>        ValueList<Vs...>  │
│  Pair<K, V>            Identity<T>           Constant<V>       │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
Layer 0: Language Features
┌─────────────────────────────────────────────────────────────────┐
│  Template parameters   constexpr             static_assert     │
│  Variadic templates    fold expressions      if constexpr      │
│  SFINAE / concepts     NTTP (C++20)          Deducing this     │
└─────────────────────────────────────────────────────────────────┘

4.2 Key Components

┌──────────────────────────────────────────────────────────────┐
│                    COMPONENT RELATIONSHIPS                    │
└──────────────────────────────────────────────────────────────┘

TypeList<Ts...>                      FixedString<N>
    │                                     │
    ├── Length<L>                         ├── size()
    ├── At<L, I>                          ├── operator[]
    ├── Contains<L, T>                    ├── operator+
    ├── IndexOf<L, T>                     ├── operator==
    │                                     └── substr<Start, Len>
    │                                         │
    ├── PushFront<L, T>                       │
    ├── PushBack<L, T>                        ▼
    ├── PopFront<L>                    ┌─────────────────┐
    ├── PopBack<L>                     │  JSON Parser    │
    │                                  │                 │
    ├── Concat<L1, L2>                 │ parseValue()    │
    ├── Reverse<L>                     │ parseObject()   │
    ├── Unique<L>                      │ parseArray()    │
    │                                  │ parseString()   │
    └── Higher-Order                   │ parseNumber()   │
        ├── Filter<L, Pred>            └─────────────────┘
        ├── Transform<L, F>
        ├── Fold<L, F, Init>
        └── ForEach<L, F>

    Uses                                   Uses
      │                                      │
      ▼                                      ▼
┌─────────────────┐                  ┌─────────────────┐
│  Type Traits    │                  │  Format Check   │
│                 │                  │                 │
│  is_type_list   │                  │  parse specs    │
│  is_complete    │                  │  match types    │
│  has_member_x   │                  │  emit errors    │
└─────────────────┘                  └─────────────────┘

4.3 Data Structures

TypeList:

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

FixedString (Non-Type Template Parameter):

template<size_t N>
struct FixedString {
    char data[N];
    constexpr FixedString(const char (&str)[N]);
    constexpr size_t size() const { return N - 1; }
    constexpr char operator[](size_t i) const { return data[i]; }
};

ValueList:

template<auto... Vs>
struct ValueList {
    static constexpr size_t size = sizeof...(Vs);
};

4.4 Algorithm Overview

Recursive Type Operations:

At<TypeList<A, B, C>, 1>
    │
    ├── Match: At<TypeList<Head, Tail...>, 0> → Head
    │
    └── Recurse: At<TypeList<Head, Tail...>, N>
                 → At<TypeList<Tail...>, N-1>

So: At<TypeList<A, B, C>, 1>
    → At<TypeList<B, C>, 0>
    → B

Filter with Predicate:

Filter<TypeList<int, float, double>, is_integral>
    │
    ├── For each T in list:
    │   └── if Pred<T>::value → include T
    │
    └── Concat results

So: Filter<TypeList<int, float, double>, is_integral>
    → TypeList<int>

5. Implementation Guide

5.1 Development Environment Setup

# Required: C++17 minimum, C++20 recommended
g++ --version  # Need GCC 10+ or Clang 10+

# Project structure
mkdir -p template_meta/include/meta
mkdir -p template_meta/tests

# CMakeLists.txt
cat > template_meta/CMakeLists.txt << 'EOF'
cmake_minimum_required(VERSION 3.20)
project(template_meta CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(tests tests/main.cpp)
target_include_directories(tests PRIVATE include)
EOF

5.2 Project Structure

template_meta/
├── CMakeLists.txt
├── include/
│   └── meta/
│       ├── typelist.hpp         # TypeList and operations
│       ├── valuelist.hpp        # ValueList and operations
│       ├── traits.hpp           # Extended type traits
│       ├── fixed_string.hpp     # Compile-time strings
│       ├── json.hpp             # Compile-time JSON
│       ├── format.hpp           # Type-safe format strings
│       └── meta.hpp             # All-in-one include
└── tests/
    ├── main.cpp
    ├── test_typelist.cpp
    ├── test_string.cpp
    └── test_json.cpp

5.3 The Core Question You’re Answering

“How can I compute with types instead of values, and perform all computation before my program ever runs?”

This is about understanding that:

  • Types can be values in a different dimension
  • Templates are functions that take types and return types
  • Specialization is pattern matching
  • The compiler is your runtime

5.4 Concepts You Must Understand First

Concept Self-Assessment Question Resource
Variadic templates Can you write a function taking any number of arguments? “C++ Templates” Ch. 4
Parameter packs What does sizeof...(Ts) return? cppreference
Template specialization How do you specialize for a specific type? “C++ Templates” Ch. 2
SFINAE Why does enable_if work? “C++ Templates” Ch. 8
Concepts (C++20) How do you constrain a template? “C++20” by Josuttis
constexpr What can be computed at compile time? “EMC++” Item 15
Fold expressions How do you sum a parameter pack? cppreference

5.5 Questions to Guide Your Design

Type List Design:

  • How do you store an arbitrary number of types?
  • How do you access the Nth type (recursive vs fold)?
  • How do you express “type → type” transformations?
  • How do you handle empty lists?

Compile-Time Computation:

  • What makes a function constexpr-eligible?
  • How do you force compile-time evaluation with consteval?
  • How do you iterate characters in a string at compile time?
  • How do you accumulate results (fold expressions)?

Error Messages:

  • How do you make template errors readable?
  • How do you use static_assert effectively?
  • How do concepts improve error messages?

5.6 Thinking Exercise

Before coding, trace through this mentally:

template<typename... Ts> struct TypeList {};

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

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

// What is At<TypeList<int, double, char>, 2>::type?

Trace the instantiation chain:

  1. At<TypeList<int, double, char>, 2>
  2. → matches partial specialization with I (I != 0)
  3. At<TypeList<double, char>, 1>::type
  4. → matches partial specialization with I (I != 0)
  5. At<TypeList<char>, 0>::type
  6. → matches specialization with I = 0
  7. type = char

5.7 Hints in Layers

Hint 1: Start with TypeList basics

Begin with just TypeList<Ts...> and Length<List>.
Get static_assert(Length<TypeList<int, double>>::value == 2) working.
This proves your recursive pattern works.

Hint 2: Pattern matching with specialization

The key insight: partial specialization = pattern matching.
template<typename Head, typename... Tail>
struct At<TypeList<Head, Tail...>, 0> { using type = Head; };

This ONLY matches when the second parameter is 0.
The general case handles I > 0 by recursing.

Hint 3: Higher-order metafunctions

Filter needs to call a predicate on each type:
template<template<typename> class Pred>  // <- template template parameter

Transform needs to apply a metafunction:
template<template<typename> class F>

These take "metafunctions" (template templates) as parameters.

Hint 4: FixedString as NTTP

C++20 allows class types as non-type template parameters if they're
"structural" (all public members, literal type).

template<size_t N>
struct FixedString {
    char data[N];
    constexpr FixedString(const char (&str)[N]) {
        for (size_t i = 0; i < N; i++) data[i] = str[i];
    }
};

// Deduction guide
template<size_t N>
FixedString(const char (&)[N]) -> FixedString<N>;

// Now use as template parameter:
template<FixedString S>
void func() { /* S.data is the string */ }

5.8 The Interview Questions They’ll Ask

  1. “Explain SFINAE and give an example.”
    • Expected: Substitution Failure Is Not An Error. When template substitution fails, that overload is removed from consideration instead of being an error. Example: enable_if.
  2. “What’s the difference between constexpr and consteval?”
    • Expected: constexpr can be evaluated at compile time if possible. consteval must be evaluated at compile time.
  3. “How would you implement a compile-time type list?”
    • Expected: Variadic template class TypeList<Ts...>, with operations implemented via partial specialization to extract head/tail.
  4. “What are concepts and how do they improve on SFINAE?”
    • Expected: Concepts are named constraints on template parameters. They produce clearer error messages and are easier to write than SFINAE tricks.
  5. “What is the difference between a template and a metafunction?”
    • Expected: A metafunction is a template that represents a type → type or type → value mapping. It has a standard interface (::type or ::value).
  6. “How would you detect if a type has a member function foo()?”
    • Expected: Use decltype and SFINAE: decltype(std::declval<T>().foo()). Or with concepts: requires(T t) { t.foo(); }.
  7. “What are fold expressions?”
    • Expected: C++17 feature to reduce a parameter pack with an operator: (... + args) sums all args.

5.9 Books That Will Help

Topic Book Chapter
Variadic Templates “C++ Templates: The Complete Guide” Chapter 4
Template Argument Deduction “C++ Templates: The Complete Guide” Chapter 15
SFINAE “C++ Templates: The Complete Guide” Chapter 8
Type Traits “C++ Templates: The Complete Guide” Chapter 19
Concepts “C++20 - The Complete Guide” Chapter 5
constexpr “Effective Modern C++” Item 15
Expression Templates “C++ Templates: The Complete Guide” Chapter 27
Tuples “C++ Templates: The Complete Guide” Chapter 25

5.10 Implementation Phases

Phase 1: TypeList Basics (Week 1)

// Milestone: These static_asserts pass
using L = TypeList<int, double, char>;
static_assert(Length<L>::value == 3);
static_assert(std::is_same_v<At<L, 0>, int>);
static_assert(std::is_same_v<At<L, 2>, char>);
static_assert(Contains<L, double>::value);
static_assert(!Contains<L, float>::value);

Phase 2: TypeList Transformations (Week 2)

// Milestone: Higher-order operations work
using Filtered = Filter<L, std::is_integral>;
static_assert(std::is_same_v<Filtered, TypeList<int, char>>);

using Transformed = Transform<L, std::add_pointer>;
static_assert(std::is_same_v<At<Transformed, 0>, int*>);

Phase 3: FixedString (Week 3)

// Milestone: Compile-time strings work
constexpr FixedString s1 = "Hello";
constexpr FixedString s2 = "World";
constexpr auto s3 = s1 + ", " + s2;
static_assert(s3 == "Hello, World");
static_assert(s3.size() == 12);

Phase 4: Compile-Time JSON (Weeks 4-5)

// Milestone: JSON parsing at compile time
constexpr auto j = R"({"x": 10, "y": 20})"_json;
static_assert(j["x"] == 10);
static_assert(j["y"] == 20);

Phase 5: Format Strings & Polish (Weeks 6-8)

// Milestone: Type-safe format strings
auto s = format<"Name: %s, Age: %d">("Alice", 30);
// format<"Value: %d">("wrong");  // Compile error!

5.11 Key Implementation Decisions

Type List Implementation:

// Approach 1: Recursion (traditional)
template<typename List, size_t I>
struct At {
    using type = typename At<PopFront<List>, I-1>::type;
};
template<typename List>
struct At<List, 0> {
    using type = typename List::Head;  // or similar
};

// Approach 2: Parameter pack indexing (C++17)
template<typename... Ts, size_t I>
auto at_impl(TypeList<Ts...>, std::index_sequence<I>)
    -> std::tuple_element_t<I, std::tuple<Ts...>>;

template<typename List, size_t I>
using At = decltype(at_impl(List{}, std::index_sequence<I>{}));

FixedString Concatenation:

template<size_t N1, size_t N2>
constexpr auto operator+(FixedString<N1> lhs, FixedString<N2> rhs) {
    char result[N1 + N2 - 1];  // -1 for double null terminator
    for (size_t i = 0; i < N1 - 1; i++) result[i] = lhs[i];
    for (size_t i = 0; i < N2; i++) result[N1 - 1 + i] = rhs[i];
    return FixedString<N1 + N2 - 1>(result);
}

6. Testing Strategy

Static Tests (Most Important):

// All tests are static_assert - if it compiles, it passes!
namespace tests {
    // Length
    static_assert(Length<TypeList<>>::value == 0);
    static_assert(Length<TypeList<int>>::value == 1);
    static_assert(Length<TypeList<int, double, char>>::value == 3);

    // At
    using L = TypeList<int, double, char>;
    static_assert(std::is_same_v<At<L, 0>, int>);
    static_assert(std::is_same_v<At<L, 1>, double>);
    static_assert(std::is_same_v<At<L, 2>, char>);

    // Contains
    static_assert(Contains<L, int>::value);
    static_assert(!Contains<L, float>::value);

    // Transform
    using Ptrs = Transform<L, std::add_pointer>;
    static_assert(std::is_same_v<At<Ptrs, 0>, int*>);

    // Filter
    using Ints = Filter<L, std::is_integral>;
    static_assert(Length<Ints>::value == 2);  // int, char

    // FixedString
    constexpr FixedString s = "test";
    static_assert(s.size() == 4);
    static_assert(s[0] == 't');
    static_assert(s == "test");
}

int main() {
    // If we reach here, all static tests passed
    std::cout << "All compile-time tests passed!\n";
}

Error Message Testing:

// Manually verify error messages are clear:
// Uncomment and check compiler output
// At<TypeList<int>, 5>::type x;  // Should say "index out of bounds"
// format<"%d">("string");  // Should say "expected int, got string"

7. Common Pitfalls & Debugging

Problem Symptom Root Cause Fix
Infinite template recursion Compiler hangs or stack overflow Missing base case specialization Add explicit specialization for empty list / index 0
“Incomplete type” error Can’t use ::type or ::value Accessing before instantiation Ensure all needed types are fully defined
“No matching overload” SFINAE removed all options Constraints too strict Check your enable_if / concept conditions
“Ambiguous specialization” Two specializations match Overlapping patterns Make specializations more specific
constexpr function not constexpr Can’t use in static_assert Non-constexpr operation inside Remove heap allocation, virtual calls, etc.
Template parameter deduction failed Can’t deduce types Deduction context issue Provide explicit template args or deduction guide

Debugging Tips:

  1. Print types at compile time:
    template<typename T> struct Debug;
    // Usage: Debug<SomeType>{};  // Error shows what SomeType is
    
  2. Check intermediate types:
    using Step1 = Filter<L, Pred>;
    using Step2 = Transform<Step1, F>;
    static_assert(!std::is_same_v<Step1, void>);  // Verify step 1 worked
    
  3. Use concepts for clearer errors: ```cpp template concept HasValueMember = requires { T::value; };

template void foo() { } // Error will say "constraint not satisfied" with details


---

## 8. Extensions & Challenges

### Extension 1: Compile-Time Regex
```cpp
constexpr auto pattern = regex<R"(\d+)">();
static_assert(pattern.match("123"));
static_assert(!pattern.match("abc"));

Extension 2: Type-Level State Machine

using Machine = StateMachine<
    State<"idle", Transition<"start", "running">>,
    State<"running", Transition<"stop", "idle">>
>;
static_assert(Machine::transition("idle", "start") == "running");

Extension 3: Compile-Time SQL Parser

constexpr auto query = sql<"SELECT name, age FROM users WHERE id = ?">();
static_assert(query.columns[0] == "name");
static_assert(query.table == "users");

Extension 4: Boost.Hana-style Value Semantics

constexpr auto list = make_tuple(1_c, 2_c, 3_c);  // Integral constants
constexpr auto doubled = transform(list, [](auto x) { return x * 2_c; });
static_assert(doubled[0_c] == 2_c);

9. Real-World Connections

Eigen (Linear Algebra Library):

  • Uses expression templates to fuse operations
  • (A * B + C) creates a type representing the expression
  • Evaluation is deferred until assignment

Boost.Spirit (Parser Library):

  • Parsers are types, combined with operators
  • int_ >> ',' >> double_ builds a parser type
  • Compile-time grammar checking

Boost.Hana (Metaprogramming):

  • Types have value semantics
  • hana::tuple<int, double> behaves like a runtime tuple
  • Unifies TMP with runtime programming

nlohmann/json (JSON Library):

  • Uses SFINAE/concepts for type detection
  • json j = myStruct; works via TMP

10. Resources

Documentation

Tutorials

Libraries to Study


11. Self-Assessment Checklist

TypeList Operations

  • Length<List> returns correct count
  • At<List, I> accesses correct type
  • Contains<List, T> correctly detects presence
  • IndexOf<List, T> returns correct index (or sentinel)
  • PushFront/PushBack add types correctly
  • PopFront/PopBack remove types correctly
  • Concat<L1, L2> concatenates lists
  • Filter<List, Pred> keeps matching types
  • Transform<List, F> applies metafunction
  • Unique<List> removes duplicates
  • Reverse<List> reverses order

FixedString

  • Construction from string literal works
  • size() returns correct length
  • operator[] accesses characters
  • operator+ concatenates strings
  • operator== compares strings
  • Can be used as template parameter

Compile-Time JSON

  • Parses null, bool, number, string
  • Parses arrays
  • Parses objects
  • Nested structures work
  • operator[] accesses elements

General

  • All tests are static_assert (compile-time)
  • Error messages are helpful
  • Code compiles with -Wall -Wextra -Werror

12. Submission / Completion Criteria

Your implementation is complete when:

  1. All TypeList operations work:
    using L = TypeList<int, double, char, bool>;
    static_assert(Length<L>::value == 4);
    static_assert(std::is_same_v<At<L, 1>, double>);
    static_assert(Contains<L, char>::value);
    static_assert(IndexOf<L, bool>::value == 3);
    static_assert(std::is_same_v<
     Filter<L, std::is_integral>,
     TypeList<int, char, bool>
    >);
    
  2. FixedString manipulation works:
    constexpr auto s = FixedString{"Hello"} + ", " + "World!";
    static_assert(s == "Hello, World!");
    
  3. Compile-time JSON (at least basic) works:
    constexpr auto j = R"({"x": 10})"_json;
    static_assert(j["x"] == 10);
    
  4. Zero runtime cost:
    // Assembly output shows no runtime computation
    // All values are compile-time constants
    
  5. Clean compilation:
    g++ -std=c++20 -Wall -Wextra -Werror tests.cpp
    ./a.out  # Prints "All tests passed" if it runs
    

Template metaprogramming is C++’s dark magic—computation without runtime. Master this, and you’ll understand how the STL, Boost, and high-performance libraries achieve zero-cost abstractions. Every std::tuple, std::variant, and std::optional uses these techniques.