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)
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!
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:
static_cast
dance wherever appropriate when adding more code to the class in the future!)AbstractLogger
and Logger
into two classes..cpp
file) - even the ones that do not need to do the static_cast
thing.Considering the above, I'm seeking insight from people with C++ experience:
How about the other way:
template <typename ...Ts>
class Logger : private Ts...
{
public:
using Ts::write...;
void write(int x) { /*...*/ }
void write(double x) { /*...*/ }
// ...
template <typename T>
void writeLine(T x) { write(x); /*...*/ }
// ...
};
class FooWriter
{
public:
void write(FooType1 x) { /*...*/ }
void write(FooType2 x) { /*...*/ }
};
using FooLogger = Logger<FooWriter>;
And then use any of (or their aliases):
Logger<>
or Logger<FooWriter>
or Logger<FooWriter, BarWriter>
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With