Last active
December 30, 2017 15:17
-
-
Save langthom/b6ec81479f8637a99f81651ac133be7d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// g++ -Wall -std=c++11 -o crtp CuriouslyRecurringTemplatePattern.cc | |
// Curiously Recurring Template Pattern (CRTP) | |
// | |
// When this is used, a child class inherits from a base class templated with the own class. | |
#include <iostream> | |
// Example 1: A Counter for a class instances. | |
// The base class simply counts the created instances as a static counter. | |
// Here the template type is important: | |
// The compiler instantiates one class for each type, therefore not all | |
// "counted" classes use the same counter, what would be wrong. | |
template<typename ChildClass> | |
struct Counter { | |
static int instancesCreated; | |
Counter() { | |
++instancesCreated; | |
} | |
}; | |
// Static initialization, as always. | |
template<typename T> int Counter<T>::instancesCreated = 0; | |
// Now use it in two classes. | |
struct ChildOne : Counter<ChildOne> {}; | |
struct ChildTwo : Counter<ChildTwo> {}; | |
////////////////////////////////////////////////////////////////////////////////////////// | |
// Example 1: A printer. | |
class InvalidPrinter { | |
public: | |
InvalidPrinter(std::ostream& str) : stream(str) {} | |
template<typename T> | |
InvalidPrinter& println(T&& instance) { | |
stream << instance << std::endl; | |
return *this; | |
} | |
protected: | |
template<typename T> | |
InvalidPrinter& print(T&& instance) { | |
stream << instance; | |
return *this; | |
} | |
private: | |
std::ostream& stream; | |
}; | |
class InvalidCoutPrinter : public InvalidPrinter { | |
public: | |
InvalidCoutPrinter() : InvalidPrinter(std::cout) {} | |
InvalidCoutPrinter& setColor(const std::string& format) { | |
print(format); | |
return *this; | |
} | |
}; | |
// correction | |
template<typename ConcretePrinter> | |
class CorrectPrinter { | |
public: | |
CorrectPrinter(std::ostream& str) : stream(str) {} | |
template<typename T> | |
ConcretePrinter& println(T&& instance) { | |
stream << instance << std::endl; | |
return static_cast<ConcretePrinter&>(*this); | |
} | |
protected: | |
template<typename T> | |
ConcretePrinter& print(T&& instance) { | |
stream << instance; | |
return static_cast<ConcretePrinter&>(*this); | |
} | |
private: | |
std::ostream& stream; | |
}; | |
class CorrectCoutPrinter : public CorrectPrinter<CorrectCoutPrinter> { | |
public: | |
CorrectCoutPrinter() : CorrectPrinter(std::cout) {} | |
CorrectCoutPrinter& setColor(const std::string& format) { | |
print(format); | |
return *this; | |
} | |
}; | |
void testPrinter(void) { | |
// Using a base printer, everything is fine. | |
InvalidPrinter{std::cout}.println("Hello World!").println(999); | |
// Invalid: The inherited println returns a InvalidPrinter, which does not have a 'setColor' member function. | |
//InvalidCoutPrinter().println("Hello World!").setColor("\033[1;31m").println(999).setColor("\033[0m"); | |
// Now the correct one: | |
// Now println returns a CorrectCoutPrinter reference, which has the member function. | |
CorrectCoutPrinter().println("Hello World!").setColor("\033[1;31m").println(999).setColor("\033[0m"); | |
} | |
////////////////////////////////////////////////////////////////////////////////////////// | |
// The famous Barton-Nackman trick which uses CRTP and can be used to simulate type classes. | |
template<typename T> | |
struct Comparable { | |
friend bool operator==(const T& lhs, const T& rhs) { return lhs.equals(rhs); } | |
friend bool operator!=(const T& lhs, const T& rhs) { return !(lhs == rhs); } | |
}; | |
// The inheritance using CRTP instantiates the Comparable class with our new type. | |
// This generates the comparison operators with that type automatically, and because | |
// of the inheritance they become visible and usable. | |
struct UsesComparable : Comparable<UsesComparable> { | |
bool equals(const UsesComparable& other) const { | |
return true; | |
} | |
}; | |
////////////////////////////////////////////////////////////////////////////////////////// | |
// Test code: | |
int main(void) { | |
// Create two instances of struct "ChildOne". | |
struct ChildOne one_first, one_second; | |
// Create three instances of struct "ChildTwo". | |
struct ChildTwo two_first, two_second, two_third; | |
// Now print the counts, which will NOT be 5 as it would be without templates. | |
std::cout << "Instances of class \"ChildOne\": " << ChildOne::instancesCreated << std::endl; | |
std::cout << "Instances of class \"ChildTwo\": " << ChildTwo::instancesCreated << std::endl; | |
std::cout << "Printer tests:" << std::endl; | |
testPrinter(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment