Design Pattern

Curiously Recurring Template Pattern (CRTP)

Learned in CS247.

When to use this pattern?

There are 2 use cases:

  1. Reduce boilerplate code while maintaining compile-time polymorphism
  2. Polymorphic cloning

So it seems that with CRTP, you always have 3 layers in the hierarchy, due to the nature of the templates, which means that they are actually different superclasses, so you have to have these superclasses inherit from a higher superclass that doesn’t use templates.

Motivation

Introduced after showcasing Visitor Pattern, and how there was much code, because there is going to be a lot of methods: if we have subclasses of and subclasses of : different methods to write.

Annoying part: Boilerplate - write the following in each DerivedEnemy:

class DerivedEnemy: public Enemey {
	public:
		void beStruckBy(Weapon& w) {
			w.strike(*this);
		}
};
  • must be one for every single DerivedEnemy
  • Cannot put it in Enemy:
class Enemy {
	public:
		void beStruckBy(Weapon& w) {
			w.strike(*this); // this is just an Enemy
		}
};

However, notice that this doesn’t work - the type of this is wrong. It’s not telling us what the dynamic type is.

Solution to fixing the boilerplate code is to use Curiously Recurring Template Pattern (CRTP).

Example 1

Template our superclass with a type parameter - inherit, AND substitute the derived class type.

template<typename T> class Base {
	...
};
class Derived: public Base<Derived> { // At this point, we know Derived is a class.
	... // use T in Base as if had provided a forward declaration
 
}

How do we use this to fix the boilerplate code?

template<typename T> class Enemy {
	public:
		void beStruckBy() {
			w.strike(*static_cast<T*>(this));
		}
};
 
class Monster: public Enemy<Monster> {...};
class Turtle: public Enemy<Turtle> {...};

This sort of works

Weapon* w = ...;
Turtle t{...};
t.beStruckBy(*w); // Calls Enemy<Turtle>::beStruckBy

Cast this from type Enemy<Turtle>* to Turtle* allows us to override into Rock::Strike(Turtle&) and Stick::Strike(Turtle&).

Issue: Now, we have different superclasses for each Enemy

Because Enemy<Turtle> and Enemy<Monster> are different classes: can no longer use Enemys polymorphically - no vector<Enemy*> allowed!

Solution: Add another layer of inheritance.

class Enemy {
	public:
		virtual void beStruckBy(Weapon& w) = 0;
		virtual ~Enemy() {}
};
 
template<typename T> class EnemyBeStruck: public Enemy {
	public:
		void beStruckBy(Weapon& w) override {
			w.strike(*static_cast<T*>(this));
		}
		virtual ~EnemyBeStruck() = 0;
}
 
template<typename T> EnemyBeStruck<T>::~EnemyBeStruck<T>() {}
class Turtle: public EnemyBeStruck<Turtle> {...}
class Monster: public EnemyBeStruck<Monster> {...}

Now, we have a public interface by which all our concrete enemies follow: can all beStruckBy weapons. We use the virtual method in Enemy to resolve beStruckBy to either EnemyBeStruck<Turtle> or EnemyByStruck<Monster> . Then just static_cast to T* - and we’re good.

Weapon* w = ...;
Enemy* e = new Turtle{...} / new Monster{...};
e->beSstruckBy(*w);

Example #2

Another problem CRTP can solve: polymorphic cloning.

Recall abstract books:

Say I have:

AbstractBook* b = ...;

I want a deep copy of whatever b points to. I cannot just do this:

AbstractBook* b2 = new AbstractBook{*b};

This attempts to create an AbstractBook by invoking its constructor. Wrong for 2 reasons:

  1. AbstractBook is abstract, cannot instantiate those objects
  2. Ignoring what we’re actually pointing at, we actually want to invoke a constructor that depends on the dynamic type of b

We can provide a virtual clone method for the purpose of solving this:

class AbstractBook {
	public:
		virtual AbstractBook* clone() = 0;
		virtual ~AbstractBook() {};
 
};
 
class Text: public AbstractBook {
	public:
		Text* clone() override {
			return new Text{title, author, length, topic};
		}
};
 
// Comic and NormalBook are similar
 
AbstractBook* b = ...;
AbstractBook* b2 = b->clone();

Instead use the copy ctor in each of our clone methods to simplify the implementation.

class Text: public AbstractBook {
	public:
		Text* clone() override {
			return new Text{*this};
		}
};

Exact same code in MermaidBook and Comic - just the type of this and the type of ctor which is changing. Once again, we can use CRTP.

class AbstractBook {
	public:
		virtual AbstractBook* clone() = 0;
		virtual ~AbstractBook() {};
};
 
template<typename T> class BookClonable: public AbstractBook {
	public:
		T* clone() override {
			return new T{*static_cast<T*>(this)};
		}
		virtual ~BookClonable() = 0;
};
 
template<typename T> 
BookClonable<T>::~BookClonable<T>() {};
 
class Text: public BookClonable<Text> {...};
class Comic: public BookClonable<Comic> {...};
 
AbstractBook* b = new Text{...} / new Comic {...};
AbstractBook* b2= b->clone();

b->clone is virtual, so if b points at a Comic, we call BookClonable<Comic>::clone - static_cast this into a Comic* and invoke the Comic copy constructor with the Comic&.

Provided for all subclasses - reduces boilerplate code.