Introduction
The Template Pattern defined in the GoF Design Patterns book, is unrelated to C++ templates and is a behavioral pattern. The Curiously Recurring Template Pattern (CRTP) is an improvement on the Template Pattern and is a C++ idiom in which a class X derives from a class template instantiation using X itself as template argument. The name of this idiom was coined by Jim Coplien, who had observed it in some of the earliest C++ template code. This technique achieves a similar effect to the use of virtual functions, without the costs (and some flexibility) of dynamic polymorphism. CRTP can be used in place of Template Pattern, provided that dynamic polymorphism is not required at runtime. This pattern is used extensively in the Windows ATL and WTL libraries.
Template Pattern
Let us look at the vanilla Template Pattern first. The Template Pattern makes use of polymorphism and a template method to do its work. In our example, the abstract base class, AbstractTextout
, has 11 overloaded Print
functions and a pure virtual function, Process
which is only implemented in the derived class. Examples of possible useful derived class could be logging, console output and debug printing. Only debug printing class is implemented.
class AbstractTextout
{
public:
void Print(const wchar_t* fmt);
protected:
virtual void Process(const std::wstring& str) = 0;
};
The code for one of the Print
functions is shown below. Box
argument is responsible for converting the POD to string. The difference with the rest of the overloaded Print
functions are just pushing back more Box
arguments into the vs
. I would not cover the Box
class as it is not the focus of this tip/trick. Readers may download the source code from the tip.
void AbstractTextout::Print(const wchar_t* fmt, Box D1)
{
std::wstring wsfmtstr = fmt;
std::vector<std::wstring> vs;
vs.push_back( D1.ToString() );
std::wstring str = StrReplace( wsfmtstr, vs );
Process(str); }
This is how the derived class, DebugPrint
, implemented the Process
function in DebugPrint.cpp.
void Process(const std::wstring& str)
{
#ifdef _DEBUG
OutputDebugStringW( str.c_str() );
#endif
}
This is how DebugPrint
class is used.
#include "DebugPrint.h"
void main()
{
DebugPrint dp;
dp.Print(L"Product:{0}, Qty:{1}, Price is ${2}\n", L"Shampoo", 1200, 2.65);
}
A vtbl is implemented for class with virtual functions. There is an overhead of the vtbl to determine the correct function to call. Curiously Recurring Template Pattern is using static polymorphism to eliminate this overhead. Let us see how it is achieved in the following section.
Curiously Recurring Template Pattern
AbstractTextout
is now a template class which means all the code defined in the cpp has to move to the header file. Before calling Process
, the code will cast itself to the derived type first.
template <typename Derived>
class AbstractTextout
{
public:
void Print(const wchar_t* fmt, Box D1 )
{
std::wstring wsfmtstr = fmt;
std::vector<std::wstring> vs;
vs.push_back( D1.ToString() );
std::wstring str = StrReplace( wsfmtstr, vs );
static_cast<Derived*>(this)->Process(str);
}
}
DebugPrint
is unchanged, except itself is a template type in its base class, AbstractTextout
and I have to make Process
non-virtual and move it to header file.
class DebugPrint : public AbstractTextout<DebugPrint>
{
public:
void Process(const std::wstring& str)
{
#ifdef _DEBUG
OutputDebugStringW( str.c_str() );
#endif
}
};
Usage of DebugPrint
class remains unchanged.
#include "DebugPrint.h"
void main()
{
DebugPrint dp;
dp.Print(L"Product:{0}, Qty:{1}, Price is ${2}\n", L"Shampoo", 1200, 2.65);
}
Related Links