mardi 17 mai 2016

C++ pattern: 1x base class + Nx derived classes BUT with a _last resort_ derived class

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:

  1. Logger class (non relevant here)
  2. 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)

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:

  • LoggerContextNone should derive from LoggerContext, because we need getGeneral()
  • LoggerContextNone should not be instantiable outside getInstance (singleton)
  • the base class should have no empty constructor, BUT the derived class should have one
  • LoggerContextNone should not call the super constructor, otherwise it would throw an error ELoggerContext
  • any derived class from LoggerContext should not be able to override getGeneral()

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