I'm trying to implement a logger with 3 levels of information: general (date/time), context, message
To reach this goal I'm trying to implemnet the following pattern:
- Logger class (non relevant here)
- Context class
- Base class
LoggerContext, this has the functionality of generating the general level infos - Derived class, which add the context specific infos (specific for a part of the application)
- Base class
The interesting part starts as I try to have a none context. That is, if the Logger is called without context, then a singleton LoggerContextNone should be used.
Here my code, that regardless of how I turn it, does not compile:
#include <string>
#include <iostream>
#include <stdexcept>
using namespace std;
enum class LoggerArea {
LOGGER_NONE, LOGGER_DOWNLOAD, LOGGER_COMPUTE,
};
class ELoggerContext: std::runtime_error {
using std::runtime_error::runtime_error;
};
class LoggerContextNone; // forward declaration, only needed for
// the commented version of the code
class LoggerContext {
protected:
LoggerArea mLA;
public:
LoggerContext(LoggerArea la);
virtual ~LoggerContext() = 0;
/*
static LoggerContext& getEmptyContext() {
static LoggerContextNone loggerContextNone = { LoggerArea::LOGGER_NONE };
return loggerContextNone;
}
*/
std::string getGeneral();
virtual std::string getContext() = 0; // pure virtual
};
string LoggerContext::getGeneral() {
return "general informations";
}
LoggerContext::LoggerContext(LoggerArea la) :
mLA(la) {
if (la == LoggerArea::LOGGER_NONE) {
throw ELoggerContext("LOGGER_NONE cannot be instantiated");
}
}
class LoggerContextNone : LoggerContext {
private:
LoggerContextNone() {
mLA = LoggerArea::LOGGER_NONE;
}
public:
virtual ~LoggerContextNone() override {
}
virtual std::string getContext() override {
return " ";
}
static LoggerContextNone& getInstance() {
static LoggerContextNone instance {};
return instance;
}
};
int main() {
// this should not be compilable:
LoggerContextNone n{LoggerArea::LOGGER_NONE};
// this should at least throw an error at run time:
LoggerContext n{LoggerArea::LOGGER_NONE};
return 0;
}
Goals:
LoggerContextNoneshould derive fromLoggerContext, because we needgetGeneral()LoggerContextNoneshould not be instantiable outside getInstance (singleton)- the base class should have no empty constructor, BUT the derived class should have one
LoggerContextNoneshould not call the super constructor, otherwise it would throw an errorELoggerContext- any derived class from
LoggerContextshould not be able to overridegetGeneral()
Is it actually possible to achieve this in C++? I'm looking for a clean solution (no factory, ...)
The compiler's error are:
19_virtual_class.cpp: In constructor ‘LoggerContextNone::LoggerContextNone()’:
19_virtual_class.cpp:45:22: error: no matching function for call to ‘LoggerContext::LoggerContext()’
LoggerContextNone() {
^
[...]
19_virtual_class.cpp: In function ‘int main()’:
19_virtual_class.cpp:62:46: error: no matching function for call to ‘LoggerContextNone::LoggerContextNone(<brace-enclosed initializer list>)’
LoggerContextNone n{LoggerArea::LOGGER_NONE};
^
Final note: This pattern seems me conceptually simple: Many class's derived from a base class with additionally a default class.
Aucun commentaire:
Enregistrer un commentaire