Modern C++ Design. Generic programming and Design Patterns Applied by Andrei Alexandrescu
Yes, the book is from 2001. We’re the same age. And yes, it mostly discusses solutions that are already familiar to everyone today and have been part of the standard library for ages. But there’s a catch…
Contents:
- Policy-Based Class Design.
The chapter discusses the challenges of designing high-quality, customizable generic classes, and why multiple inheritance doesn’t solve the problem of the exponential growth of possible variations of your components. It also includes examples of using different policies. That’s actually a theme that runs throughout the entire book.
At the end, the chapter discusses how to decompose your class into policies. There’s a piece of advice that goes like this: “Anything that can be done in more than one way should be identified and migrated from the class to a policy.” It’s important to remember that the book is written for people designing generic libraries. For a general solution in production code, the better advice is probably: “Extract what needs to be extracted right now.” Not in advance, but when the need actually arises.
- Techniques.
The second chapter covers a collection of individual utilities and language features that can be used as building blocks in more general solutions. Some of them are:
- compile-time assertions (before
static_assert) - partial template specialization
- integral constant to type (the ancestor of
std::integral_constant) - type-to-type mapping
- type selection (
std::conditional) - TypeTraits (although here it’s a class containing constants, rather than the template structs we have today).
- Typelists.
Typelists, of course, don’t support an arbitrary number of arguments (because variadic templates weren’t a thing yet). They look like this:
typelist<type1, typelist<type2, type3>>
The chapter then implements various operations on typelists and discusses how the class can be used.
-
Discusses the problems with the standard allocator and, after introducing some concepts, explains the implementation of a small-object allocator.
-
About the Command pattern and a generalized functor for its implementation. Essentially,
std::function. -
Discusses the implementation of the Singleton.
It goes into quite a lot of detail about the problems with different approaches. Eventually, we arrive at the Meyers Singleton. After a small ChatGPT-like fact-check, it turned out that it was this book where Andrei Alexandrescu gave the world this name.
He then discusses the self-named KDL problem (which can easily occur in practice) and comes up with the Phoenix Singleton to solve it.
Towards the end of the chapter, the implementation of a Singleton for the multithreaded case and a general policy-based implementation are discussed.
- About smart pointers.
What they are, how to use them, and how they should be implemented from the perspective of various practical use cases.
8 and 9. About factories and abstract factories.
-
Dedicated to the Visitor pattern.
-
Implementations of multimethods.
It’s basically function overloading that knows a little more than it’s supposed to. In short, it can be explained like this:
class Shape {};
class Asteroid : public Shape {};
class Spaceship : public Shape {};
void Collide(Asteroid& a, Spaceship& s) { /* Logic 1 */ }
void Collide(Shape& s1, Shape& s2) { /* Logic 2 */ }
And normally, you would call Collide based on the type of reference that was passed.
If an Asteroid was passed as a Shape, the generic Shape function would be called.
Multimethods, on the other hand, let you determine which object is actually in memory and call the corresponding overload.
Each chapter goes from some simple implementations to more advanced ones by looking at the mistakes and problems that arise from careless design.
Today, of course, the book looks more like a historical artifact. But!
- The first chapter on policy-based design is still relevant because it discusses conceptual things. It really stuck with me and helped me look at some concepts differently.
- Most of the other chapters are useful for understanding how to approach the design of generic solutions.
So, if you don’t treat the book as a C++ reference, but instead try to see it as a guide to design through the examples of specific problems, it can still be quite useful.