Гибкий класс логгера с использованием стандартных потоков в C ++ - PullRequest
5 голосов
/ 02 июля 2010

Я хотел бы создать гибкий класс регистратора.Я хочу, чтобы он мог выводить данные в файл или на стандартный вывод.Также я хочу использовать потоки.Класс должен выглядеть примерно так:

class Logger
{
private:
   std::ostream m_out; // or ofstream, iostream? i don't know
public:

   void useFile( std::string fname);
   void useStdOut();

   void log( symbol_id si, int val );
   void log( symbol_id si, std::string str );
   //etc..
};

symbol_id является перечислением и определяет форматирование.Чего я хочу добиться, так это возможности легко переключаться со стандартного вывода в файл и наоборот (это цель методов use*).Желательно просто используя m_out и просто писать m_out << "something"; без каких-либо проверок, хочу ли я записать в файл или стандартный вывод.

Я знаю, что существует множество способов обойти это (используя if's для проверки, хочу ли я записать в файл или стандартный вывод, «путь C» (используя FILE* и fprintf)) и т. д., но я уверен, что должен быть способ какдобиться этого с помощью потоков C ++ хорошим способом.Но я не могу найти способ, как это сделать.Может кто-нибудь помочь мне, пожалуйста?

Ответы [ 4 ]

9 голосов
/ 02 июля 2010

Классы std::o*stream в C ++ наследуются от std :: ostream.Это означает, что вы должны написать свой интерфейс в зависимости от указателя или ссылки std :: ofstream:

class Logger
{
    std::ostream *m_out; // use pointer so you can change it at any point
    bool          m_owner;
public:
    // constructor is trivial (and ommited)
    virtual ~Logger()
    {
        setStream(0, false);
    }
    void setStream( std::ostream* stream, bool owner )
    {
        if(m_owner)
            delete m_out;
        m_out = stream;
        m_owner = owner;
    }
    template<typename T>
    Logger& operator << (const T& object)
    {
        if(!m_out)
            throw std::runtime_error("No stream set for Logger class");
        (*m_out) << object;
        return *this;
    }
};

// usage:
Logger logger;
logger.setStream( &std::cout, false ); // do not delete std::cout when finished
logger << "This will be logged to std::cout" << std::endl;
// ...
logger.setStream( 
    new std::ofstream("myfile.log", std::ios_base::ate|std::ios_base::app), 
    true ); // delete the file stream when Logger goes out of scope
logger << "This will be appended to myfile.log" << std::endl;
8 голосов
/ 02 июля 2010

Способ, которым я раньше атаковал эту проблему, - сделать Logger абстрактным базовым классом и создать отдельные классы FileLogger и OutStreamLogger.Затем создайте объект CompositeLogger, который реализует интерфейс Logger, который просто выводит все логгеры:

CompositeLogger compLogger;
compLogger.Add(new FileLogger("output.txt"));
compLogger.Add(new StreamLogger(std::cout));
...
compLogger.log(...);

Если вам не нужен этот уровень гибкости и вы хотите сохранить все это в одном классеВы можете сделать переменную m_Out указателем на std::ostream и добавить дополнительный флаг, чтобы отслеживать, нужно ли удалять его при очистке:

private:
  std::ostream*   m_out;
  bool            m_OwnsStream;

Logger() {
   m_Out=&std::cout; // defaults to stdout
   m_OwnsStream=false;
}
void useFile(std::string filename) {
  m_Out=new std::ofstream(filename);
  m_OwnsStream=true;
}
~Logger() {
  if (m_OwnStream) 
    delete m_Out; 
}

Очевидно, вам понадобятся еще несколько проверок в useFile и useStdOut для предотвращения утечек памяти.

4 голосов
/ 02 июля 2010

Dr.Доббс опубликовал статью, которую я использовал как источник вдохновения.Это стоит прочитать.http://www.drdobbs.com/cpp/201804215

Похоже, другая статья была опубликована совсем недавно, но я ее не читалhttp://www.drdobbs.com/cpp/225700666

2 голосов
/ 02 июля 2010

Вариант решения the_mandrill, для которого я думал, что шаблон Стратегии лучше подойдет для этой проблемы, концептуально.
Мы можем изменить стратегию ведения журнала в любое время, просто вызвав context-> SetLogger.
Мы также можемиспользовать разные файлы для файлового регистратора.

class Logger
{
protected:
    ostream* m_os;
public:
    void Log(const string& _s)
    {
        (*m_os) << _s;
        m_os->flush();
    }
};

class FileLogger : public Logger
{
    string m_filename;
public:
    explicit FileLogger(const string& _s)
    : m_filename(_s)
    {
        m_os = new ofstream(m_filename.c_str());
    }
    ~FileLogger()
    {
        if (m_os)
        {
            ofstream* of = static_cast<ofstream*>(m_os);
            of->close();
            delete m_os;
        }
    }
};

class StdOutLogger : public Logger
{
public:
    StdOutLogger()
    {
        m_os = &std::cout;    
    }
};

class Context
{
    Logger* m_logger;
public:
    explicit Context(Logger* _l)  {m_logger = _l;}
    void SetLogger(Logger* _l)    {m_logger = _l;}
    void Log(const string& _s)
    {
        if (m_logger)
        {
            m_logger->Log(_s);
        }
    }
};

int main()
{
    string filename("log.txt");

    Logger*  fileLogger   = new FileLogger(filename);
    Logger*  stdOutLogger = new StdOutLogger();
    Context* context      = new Context(fileLogger);

    context->Log("this log out to file\n");
    context->SetLogger(stdOutLogger);
    context->Log("this log out to standard output\n");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...