What is CRTP(Curiously recurring template pattern) ?

The curiously recurring template pattern (CRTP) is an idiom in C++ in which a class X derives from a class template instantiation using X itself as template argument.

Some use cases for this pattern are static polymorphism and other metaprogramming techniques such as those described by Andrei Alexandrescu in Modern C++ Design.

General form

// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
    // methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
    // ...
};

Static polymorphism

template <class T> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<T*>(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

struct Derived : public Base<Derived>
{
    void implementation();
    static void static_sub_func();
};

Dynamic polymorphism vs. Static polymorphism(CRTP)

// http://www.geeksforgeeks.org/curiously-recurring-template-pattern-crtp-2/

// A simple C++ program to demonstrate run-time 
// polymorphism
#include <iostream>
#include <chrono>
using namespace std;

typedef std::chrono::high_resolution_clock Clock;

namespace DynamicPolymorphism {

// To store dimensions of an image
class Dimension
{
public:
    Dimension(int _X, int _Y) {mX = _X;  mY = _Y; }
private:
    int mX, mY;
};

// Base class for all image types
class Image
{
public:
    virtual void Draw() = 0;
    virtual Dimension GetDimensionInPixels() = 0;
protected:
    int dimensionX;
    int dimensionY;
};

// For Tiff Images
class TiffImage : public Image
{
public:
    void Draw() { }
    Dimension GetDimensionInPixels() {
        return Dimension(dimensionX, dimensionY);
    }
};

// There can be more derived classes like PngImage, 
// BitmapImage, etc

// Driver code that calls virtual function
int main()
{
    // An image type  
    Image* pImage = new TiffImage;

    // Store time before virtual function calls
    auto then = Clock::now();

    // Call Draw 1000 times to make sure performance
    // is visible
    for (int i = 0; i < 1000; ++i)
        pImage->Draw();

    // Store time after virtual function calls
    auto now = Clock::now();

    cout << "Dynamic Polymorphism - Time taken: "
         << std::chrono::duration_cast
           <std::chrono::nanoseconds>(now - then).count()
         << " nanoseconds" << endl;

    return 0;
}

}

//
// Converted to StaticPolymorphism
//
namespace StaticPolymorphism {
// Image program (similar to above) to demonstrate
// working of CRTP

// To store dimensions of an image
class Dimension
{
public:
    Dimension(int _X, int _Y)
    {
        mX = _X;
        mY = _Y;
    }
private:
    int mX, mY;
};

// Base class for all image types. The template
// parameter T is used to know type of derived
// class pointed by pointer.
template <class T>
class Image
{
public:
    void Draw()
    {
        // Dispatch call to exact type
        static_cast<T*> (this)->Draw();
    }
    Dimension GetDimensionInPixels()
    {
        // Dispatch call to exact type
        static_cast<T*> (this)->GetDimensionInPixels();
    }

protected:
    int dimensionX, dimensionY;
};


// For Tiff Images
class TiffImage : public Image<TiffImage>
{
public:
    void Draw()
    {
        // Uncomment this to check method dispatch
        // cout << "TiffImage::Draw() called" << endl;
    }
    Dimension GetDimensionInPixels()
    {
        return Dimension(dimensionX, dimensionY);
    }
};

// There can be more derived classes like PngImage,
// BitmapImage, etc

// Driver code
int main()
{
    // An Image type pointer pointing to Tiffimage
    Image<TiffImage>* pImage = new TiffImage;

    // Store time before virtual function calls
    auto then = Clock::now();

    // Call Draw 1000 times to make sure performance
    // is visible
    for (int i = 0; i < 1000; ++i)
        pImage->Draw();

    // Store time after virtual function calls
    auto now = Clock::now();

    cout << "Static Polymorphism  - Time taken: "
         << std::chrono::duration_cast
         <std::chrono::nanoseconds>(now - then).count()
         << " nanoseconds" << endl;

    return 0;
}
}

int main() {
    DynamicPolymorphism::main();
    StaticPolymorphism::main();
}

Results after executing

Dynamic Polymorphism - Time taken: 1105 nanoseconds
Static Polymorphism  - Time taken: 45 nanoseconds

Conclusion

Sometimes we can choose the Static polymorphism instead of the Dynamic polymorphism to enhance a performance for algorithm or your libraries.

Reference

Wikimedia: https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern http://www.geeksforgeeks.org/curiously-recurring-template-pattern-crtp-2/

results matching ""

    No results matching ""