CRTP (Curiously Recurring Template Pattern)

Español


Hello! Long time no post 😛
 
I’m hooked writing a demo for particles and random numbers (pretty simple and intuitive concept but allowed me write cool stuff :)) which make me forgot the blog a bit…
 
In the mean time I’ll brought you a ‘theory’ article. I’m talking about a nice ‘trick’ I learned a few years ago when I was first exposed to production C++ code. It’s called “Curiously Recurring Template Pattern”, it’s base form looks like this:

template<class T>
class Base {
};

class Derived : public Base<Derived> {
};

You can clearly see why it’s templated & recurring (Derived inherits from an instantiation of the Base template with the Derived type itself).
 
Now, what are the uses of this?
The first use I saw was to create generalized patterns that can declare behavioral interfaces without injecting themselves in the derived classes or, with an example, a cool way to implement a Singleton. If you remember my other post, this is the trick that makes the Singleton tick. Once a class like Logger takes inheritance from the pattern, it can use their functions, and refer to himself as Logger at the same time.
 
What I mean is, Logger can have all the logic to implement logging, and if you want a singleton Logger, the only extra need will be make it inherit from the pattern. The CRTP behavior (being able to obtain the derived pointer from the base class) makes it transparent, no need for “SingletonLogger” object inheriting logic from both, or some weird casting. Each Singleton is identified by its real type, Singleton is just an attached feature. Remember from the Singleton post (simplified):

class Singleton {
public:
    template<typename... Base_Type_Arguments>
    static void init(Base_Type_Arguments&& ...args) {
        m_pInst = new Base_Type(std::forward<Base_Type_Arguments>(args)...);
    }
    static Base_Type& get() {
        return *m_pInst;
    }
private:
    static Base_Type* m_pInst;
};

With this, a class like Logger can be declared as

class Logger : public Singleton<Logger> {
public:
    Logger(bool writeToFile);
    template<class T>
    void log(T&& obj) { /**/ }
/* Rest of the logger code, without a single reference to a singleton feature*/
};

instantiated as:

Logger::init(false/*writeToFile*/);

and obtained & used as

Logger::get().log(someObject);

All of that without a single reference to Singleton.
 
As you can see I really liked this pattern, it has other uses you can look at them in the wiki article or this StackOverflow answer (which also picks the singleton pattern as another example), there is no point in repeating them since there are very well explained there.
This time there is no new source, but you can look at the singleton article and there you will find the source code for the singleton implementation.
Hope you guys like the article and the CRTP too! 😉

Back to Top




¡Buenas! Pasó rato sin artículo 😛
 
Estoy bastante enganchado escribiendo una demo de partículas y números aleatorios (nada raro, un concepto simple e intuitivo que usé como excusa para escribir algo de código :)) lo que me hizo olvidarme un poco del blog…
 
Mientras eso llega, les traigo un artículo ‘teórico’. Hablo de un lindo truco que aprendí hace algunos años cuando empecé a conocer código C++ de producción. Se llama “Curiously Recurring Template Pattern” y su forma básica es algo así:

template<class T>
class Base {
};

class Derived : public Base<Derived> {
};

Pueden ver claramente porqué es “template” y “recurring” (Derived hereda de una instanciación del template Base con Derived como el parámetro de tipo).
 
Ahora, ¿para qué se puede utilizar esto?
La primera vez que lo vi, fue creando patrones generalizados que declaran interfaces de comportamiento sin entrometerse en la clase derivada. O, con un ejemplo, una forma interesante de implementar un Singleton. Si se acuerdan mi otro artículo, este es el truco que hace que el Singleton funcione. En el momento en que otra clase como Logger pasa a heredar de este patrón, puede utilizar todas sus funciones, y referirse a sí misma como Logger al mismo tiempo.
 
A lo que voy es que, Logger puede tener toda la lógica referida a loggear, y si uno quiere tener un Singleton que loguee la única necesidad extra será hacerlo heredar del patrón definido. El comportamiento que permite CRTP (ser capaz de obtener un puntero a la clase derivada desde la clase base) hace que todo esto sea transparente, que no haya necesidad de un objeto “SingletonLogger” que herede lógica de ambos o algún casteo raro en medio. Cada singleton se indetifica por su tipo real, Singleton es solo una cualidad adquirida. Si recuerdan el Singleton del artículo pasado (simplificado):

class Singleton {
public:
    template<typename... Base_Type_Arguments>
    static void init(Base_Type_Arguments&& ...args) {
        m_pInst = new Base_Type(std::forward<Base_Type_Arguments>(args)...);
    }
    static Base_Type& get() {
        return *m_pInst;
    }
private:
    static Base_Type* m_pInst;
};

Con esto, una clase como Logger puede declararse como:

class Logger : public Singleton<Logger> {
public:
    Logger(bool writeToFile);
    template<class T>
    void log(T&& obj) { /**/ }
/* Rest of the logger code, without a single reference to a singleton feature*/
};

instanciarse como:

Logger::init(false/*writeToFile*/);

y obtenerse y usarse como:

Logger::get().log(someObject);

Todo sin una mención a Singleton.
 
Como ven, me gusta el patrón CRTP, tiene algunos otros usos, como un contador de objetos creados y destruidos, polimorfismo estático, definición de operaciones selectivas, etc… Pueden verlos en Wikipedia o esta respuesta de StackOverflow (no se preocupen por el lenguaje, es fácil entender el código), no creo que haya necesidad de repetir todo acá por ello.
Esta vez no hay fuentes nuevos, pero pueden mirar el artículo anterior de singleton y ahí van a encontrar el código fuente de la implementación de Singleton.
¡Espero que el artículo haya gustado y lo mismo con CRTP! 😉
Inicio

Advertisements

Leave a Reply (Deja una respuesta)

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s