Scenario
Consider a class Logger
which has a member function write()
overloaded for standard C++ types, and also has some convenience function-templates like writeLine()
which internally call write()
:
class Logger {
public:
void write(int x) { ... }
void write(double x) { ... }
...
template <typename T>
void writeLine(T x) { write(x); ... }
...
};
Consider further a subclass FooLogger
which adds additional write()
overloads for domain-specifc types (let's call two of them FooType1
and FooType2
):
class FooLogger : public Logger {
public:
using Logger::write;
void write(FooType1 x) { ... }
void write(FooType2 x) { ... }
...
};
(self-contained example program at Ideone)
Problem
FooLogger::write()
, when called directly, now supports any argument for which either of the two classes provides an overload.
However, FooLogger::writeLine()
only supports the argument types for which class Logger
has a write()
overload... it does not see the additional write()
overloads declared in class FooLogger
.
I want it to see them though, so that it can be called with those argument types as well!
Current solution
I got it to work using the Curiously Recurring Template Pattern (CRTP):
template <typename TDerivedClass>
class AbstractLogger {
...
template <typename T>
void writeLine(T x) { static_cast<TDerivedClass*>(this)->write(x); ... }
};
class Logger : AbstractLogger {}
class FooLogger : public AbstractLogger<FooLogger> {
...
};
(self-contained example program at Ideone)
While it does the job, it came at the cost of increased code complexity and vebosity:
- It made the implementation of the base class significantly harder to read (see the Ideone link), and harder to maintain (mustn't forget to do the
static_cast
dance wherever appropriate when adding more code to the class in the future!) - It required separating
AbstractLogger
andLogger
into two classes. - Because the base-class is now a class template, the implementations of all its member functions must now be included in the header (rather than the
.cpp
file) - even the ones that do not need to do thestatic_cast
thing.
Question
Considering the above, I'm seeking insight from people with C++ experience:
- Is CRTP the right tool for this job?
- Is there another way to solve this?