In this guide, we will explore the concept of the Curiously Recurring Template Pattern (CRTP) in C++ along with multiple illustrations.
What is the Curiously Recurring Template Pattern?
Curiously Recurring Template Pattern (CRTP) is a programming methodology that employs inheritance based on templates to accomplish static polymorphism. In this approach, a template for a base class is defined by a derived class, with the derived class inheriting from the base class using itself as a template parameter. Through this template mechanism, the base class is able to interact with and modify the attributes of the derived class during the compile-time phase.
This design is applied in visitor and factory patterns when incorporating design patterns like the singleton. Additionally, it serves to offer scalability for upholding library efficiency. By bypassing virtual function dispatch, it facilitates static polymorphism.
This design pattern is primarily utilized when a superclass needs to interact with and alter the attributes and methods of a subclass. For instance, consider a superclass template named "vehicle" and a subclass named "car". The template includes a method called "drive" that interacts with the "driveAction" method implemented in the subclass. Within the main function, an instance of the "car" is created, and the "drive" method is invoked, subsequently calling the "driveAction" method. Through this approach, CRTP facilitates compile-time access for the superclass to interact with the subclasses, allowing for state polymorphism.
Applications of using CRTP
Several applications of the CRTP are as follows:
- This pattern is used in library design, mathematical, and scientific computing.
- This CRTP is used to develop frameworks and engines, which are helpful in game development.
- These are also used in embedded systems.
- CRTP is frequently used in template metaprogramming to reduce runtime overhead.
- This pattern is also used in code generation tasks.
Example 1:
Let's consider a C++ code example for implementing the Curiously Recurring Template Pattern (CRTP).
#include <iostream>
// Base class template using CRTP
using namespace std;
template <typename Derived>
class Shape {
public:
void printArea() {
cout << "Area: " << static_cast<Derived*>(this)->calculateArea() << endl;
}
};
class Circle : public Shape<Circle> {
protected:
double radius;
public:
Circle(double r) : radius(r) {}
double calculateArea() {
return 3.14 * radius * radius;
}
};
class Rectangle : public Shape<Rectangle> {
protected:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double calculateArea() {
return width * height;
}
};
int main() {
// Creating objects of Circle and Rectangle
Circle circle(5);
Rectangle rectangle(4, 6);
circle.printArea();
rectangle.printArea();
return 0;
}
Output:
Explanation:
The program above demonstrates the Curiously Recurring Template Pattern (CRTP) in C++. Within this program, there exists a base class template referred to as "shape," alongside two derived classes, namely Circle and Rectangle. When the base class, Shape, attempts to invoke the calculateArea method, it lacks knowledge of the precise implementation within its derived classes. However, this scenario becomes feasible solely by employing the CRTP methodology. Through specifying Circle as a template parameter, the base class can effectively access and execute functions defined within its derived classes.
Example 2: Using CRTP for Event Handling System
Let's consider an example to demonstrate CRTP for managing events in C++.
#include <iostream>
using namespace std;
template <typename Derived>
class EventHandler {
public:
void handleEvent() {
static_cast<Derived*>(this)->processEvent();
}
};
class MouseEventHandler : public EventHandler<MouseEventHandler> {
public:
void processEvent() {
cout << "Mouse event handled" << endl;
}
};
int main() {
MouseEventHandler mouseHandler;
mouseHandler.handleEvent();
return 0;
}
Output:
Explanation:
In the provided code snippet, the template for the base class is specified as "EventHandler" while the template for the derived class is defined as "MouseEventHandler". The derived class, "MouseEventHandler", is responsible for defining and executing the "processEvent" function. Upon instantiating an object of the "MouseEventHandler" derived class and invoking the "handleEvent" method, the "processEvent" function is executed. This functionality is achieved by specifying "MouseEventHandler" as a template argument for the base class, "EventHandler". As a result, the base class leverages this input to access and execute functions defined within its derived classes.
Example 3:
Let's consider a different C++ code example showcasing the Curiously Recurring Template Pattern for Singletons.
#include <iostream>
using namespace std;
// Base class using CRTP for Singleton pattern
template <typename Derived>
class Singleton {
public:
static Derived& getInstance() {
static Derived instance;
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
protected:
Singleton() {}
};
// Derived class implementing Singleton pattern
class MySingleton : public Singleton<MySingleton> {
public:
void doSomething() {
cout << "Singleton instance doing something..." << endl;
}
};
int main() {
MySingleton& instance = MySingleton::getInstance();
instance.doSomething(); // Accessing singleton instance using CRTP
return 0;
}
Output:
Explanation:
This script implements a sole occurrence of a class through CRTP. The program maintains a singular instance of the class during its entire execution. In this scenario, the base class template is named "Singleton," while the subclass is named "MySingleton." The subclass includes a function named "doSomething." Within the main function, we retrieve a unique instance of "MySingleton" by employing the getInstance function, and this specific instance is then utilized to invoke the "doSomething" function.