Andrei Alexandrescu's Loki library shipped in 2001 alongside his book Modern C++ Design. It was groundbreaking — policy-based design, typelists, smart pointers, abstract factories, visitors, all built with template metaprogramming that pushed C++98 to its absolute limit.
Twenty-five years later, I rewrote the whole thing in C++20.
Repo: github.com/skillman1337/modern-loki
Why bother?
The original Loki was held together with recursive template inheritance and prayer. It predates variadic templates, constexpr, concepts, std::variant, std::tuple, and standard threading. Every pattern in the book can be expressed more clearly with modern C++.
I wanted to see what Modern C++ Design looks like when you actually have modern C++.
What changed
| 2001 (original Loki) | 2025 (Modern Loki) | C++20 feature |
|---|---|---|
Recursive Typelist<H, T>
|
Variadic typelist<Ts...>
|
Parameter packs, fold expressions |
SingletonHolder with OS locks |
singleton_holder with DCLP |
std::atomic, policy-based threading |
Manual SmartPtr policies |
smart_ptr with move semantics |
Move constructors, operator<=>
|
Functor with virtual dispatch |
functor wrapping std::function
|
std::function, concepts |
AbstractFactory with virtual MI |
Tuple-based abstract_factory
|
std::tuple, fold expressions |
GenScatterHierarchy (recursive) |
Flat variadic inheritance |
public Unit<Ts>... pack expansion |
| Windows-only threading | Portable threading policies |
std::mutex, std::shared_mutex
|
Custom Tuple
|
Deleted | Just use std::tuple
|
The result is header-only, zero dependencies, and compiles on MSVC 2022, GCC 13+, and Clang 17+.
The part I'm most proud of: documentation that compiles
Every component has its own Markdown file with an API table and 10 real-world code examples. That's 150 examples total.
Here's the thing: most C++ library docs are written by someone who last tested the examples three compiler versions ago. I didn't want that.
So I wrote a Python script that:
- Extracts every
cppcode block from every.mdfile - Compiles each one against the library headers
- Runs the resulting executable
- Fails CI if anything doesn't compile or crashes
This runs in GitHub Actions on every push. If the docs lie, the build fails.
# From scripts/test_docs.py — the core loop
for i, (title, code) in enumerate(examples):
tag = f'{base}_{i}'
status, output = test_one(tag, code)
marker = 'PASS' if status == 'OK' else 'FAIL'
print(f' [{done:3d}/{total}] {marker} {basename}#{i+1} {title}')
Here's what a run looks like:
Compiler : msvc (cl.exe)
Examples : 150 across 15 files
[ 1/150] PASS abstract_factory.md#1 Basic widget factory (2.6s)
[ 2/150] PASS abstract_factory.md#2 Cross-platform UI factory (2.8s)
...
[150/150] PASS loki.md#10 Clone factory with type_traits (3.0s)
=== TOTAL: 150/150 passed (448s) ===
I've never seen another C++ library do this. If yours does, please tell me — I want to steal your approach.
Example: before and after
Here's what a typelist looked like in 2001:
// Original Loki (C++98)
typedef TYPELIST_4(int, double, char, std::string) MyTypes;
// Macro expands to:
// Typelist<int, Typelist<double, Typelist<char,
// Typelist<std::string, NullType>>>>
Here's the same thing today:
// Modern Loki (C++20)
using MyTypes = loki::typelist<int, double, char, std::string>;
static_assert(loki::tl::length_v<MyTypes> == 4);
static_assert(loki::tl::contains_v<MyTypes, double>);
static_assert(loki::tl::index_of_v<MyTypes, char> == 2);
No macros. No recursive inheritance. Just a variadic template and constexpr metafunctions.
Example: policy-based singleton
#include <loki/singleton.hpp>
#include <iostream>
#include <string>
struct AppConfig {
std::string db_host = "localhost";
int db_port = 5432;
};
// Thread-safe, DCLP, default lifetime
using Config = loki::singleton_holder<
AppConfig,
loki::create_using_new,
loki::default_lifetime,
loki::class_level_lockable
>;
// Anywhere in your code:
auto& cfg = Config::instance();
std::cout << cfg.db_host << ":" << cfg.db_port << "\n";
You pick your creation policy, lifetime policy, and threading policy independently. That's the whole point of Alexandrescu's design — and it reads a lot better in C++20 than it did with IMPLEMENT_DEFAULT_SINGLETON macros.
Is this useful in production?
Honestly? For most of it, no. Use std::unique_ptr over smart_ptr. Use std::flat_map (C++23) over assoc_vector. Use a real async runtime over the threading policies.
The README says this upfront. This is a teaching and reference library. It's for:
- Studying the patterns from Modern C++ Design in modern idioms
- Teaching template metaprogramming and policy-based design
- Using components that
std::doesn't provide (abstract factories, multi-methods, small object allocators)
Try it
git clone https://github.com/skillman1337/modern-loki.git
cd modern-loki
cmake -B build
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
Or just read the docs — every example compiles. I made sure.
If you've read Modern C++ Design, I'd love to hear which patterns you think aged well and which didn't. Drop a comment.
Top comments (0)