Given the following, working code.
#include <iostream>
template<class Detail>
class AbstractLogger
{
public:
static void log(const char* str) {
Detail::log_detailled(str);
}
};
class Logger : public AbstractLogger<Logger>
{
public:
static void log_detailled(const char* str) {
std::cerr << str << std::endl;
}
};
int main(void)
{
AbstractLogger<Logger>::log("main function running!");
return 0;
}
Now, I want to put AbstractLogger into a library, and let the library user define his own logger, like the Logger class here. This has one drawback: AbstractLogger<Logger> can not be used inside the library, since the library can not know Logger.
Notes:
- Please no virtual functions or questions why not. Also, I am aware of the similar problem that “static virtual” members are invalid. Maybe, there is a workaround in CRTP 🙂
- C++11 will be interesting, however, I need “usual” C++.
The usual approach is to code against a concept, while providing helpers so that users may easily produce types that satisfy one or more of those concepts. As an example, something like
boost::iterator_facadeis a CRTP helper that makes it easier for a user to write an iterator. Then, that iterator can be used anywhere an iterator is accepted — for instance in the range constructor ofstd::vector. Notice how that particular constructor has no foreknowledge of the user-defined type.In your case,
AbstractLoggerwould be the CRTP helper. The missing piece would be to define e.g. a logger concept. As a result, notice that everything that needs a logger either needs to be implemented as a template or you need a type-erasing container to hold arbitrary loggers.Concept checks (like those provided by Boost) are convenient for this kind of programming, since they allow to represent a concept with actual code.