2015-06-26 5 views
2

Благодаря сообществу SO мне удалось создать простую структуру регистратора. Можно использовать его operator<<, как это:Как определить конец оператора << метод?

*CLogger::instance(CLogger::ElogLevel::eInfo) << "Application directory " << Enviroment::appDir << "\n"; 

Это объект отвечает за сбор ввода:

template<typename T> 
    CLogger& operator<<(const T& t) 
    { 
     if (((int)m_logLine.logLevel <= (int)m_userDefinedLogLevel) && m_logStarted) 
     { 
      ostringstream stream; 
      stream << t; 
      m_logLine.logString += stream.str(); 

      if (stream.str().find("\n") != string::npos) 
      { 
       push_back(m_logLine); 
       m_logLine.logString.clear(); 
      } 
     } 

     return *this; 
    } 

Моя проблема заключается в том, что я не знаю, когда делал сход ввода одного экземпляра (один метод run) завершено. Как вы можете видеть в коде, я немного подтолкнул его, поскольку это единственное, о чем я мог подумать. Метод завершит сбор пользовательского ввода и отправку данных для печати (push_back(m_logLine);), когда он найдет знак \n. Вот почему на данный момент я должен закончить каждую строку журнала с помощью знака \n. Я хотел бы избежать этого, поскольку его можно легко забыть. Есть ли другой способ выяснить, что поток закончен? Буду признателен за всю помощь!

Это мой полный код: CLogger.h:

/* 
* CLogger.h 
* 
* Created on: 25 cze 2015 
*  Author: lukasz 
*/ 

#ifndef CLOGGER_H_ 
#define CLOGGER_H_ 

#include <iostream> 
#include <deque> 
#include <string> 
#include <mutex> 
#include <condition_variable> 
#include <pthread.h> 
#include <ostream> 
#include <fstream> 
#include <sstream> 
#include <ctime> 

using namespace std; 

class CLogger 
{ 
public: 
    enum class ElogLevel { eNone = 0, eError, eWarning, eInfo, eDebug }; 

    typedef struct 
    { 
     string logString; 
     ElogLevel logLevel; 
    } logline_t; 

    static CLogger* instance(ElogLevel ll = ElogLevel::eError); 

    bool startLog(string fileName, bool verbose); 
    logline_t pop_front(); 
    void push_back(logline_t s); 
    void setLogLevel(ElogLevel ll); 

    template<typename T> 
    CLogger& operator<<(const T& t) 
    { 
     if (((int)m_logLine.logLevel <= (int)m_userDefinedLogLevel) && m_logStarted) 
     { 
      ostringstream stream; 
      stream << t; 
      m_logLine.logString += stream.str(); 

      if (stream.str().find("\n") != string::npos) 
      { 
       push_back(m_logLine); 
       m_logLine.logString.clear(); 
      } 
     } 

     return *this; 
    } 

protected: 
    virtual void threadLoop(); 

private: 
    CLogger() {};        // Private so that it can not be called 
    CLogger(CLogger const&) {};     // copy constructor is private 
    CLogger& operator= (CLogger const&) {};  // assignment operator is private 
    static CLogger* mp_instance; 
    bool m_logStarted; 
    logline_t m_logLine; 
    ElogLevel m_userDefinedLogLevel; 
    ofstream m_logFileStream; 
    bool m_verbose; 

    static void * threadHelper(void* handler) 
    { 
     ((CLogger*)handler)->threadLoop(); 
     return NULL; 
    } 

    deque<logline_t> m_data; 
    mutex m_mutex; 
    condition_variable m_cv; 
    pthread_t thread; 
}; 

#endif /* CLOGGER_H_ */ 

CLogger.cpp:

/* 
* CLogger.cpp 
* 
* Created on: 25 cze 2015 
*  Author: lukasz 
*/ 

#include "CLogger.h" 

using namespace std; 

// Global static pointer used to ensure a single instance of the class. 
CLogger* CLogger::mp_instance = NULL; 

/** This function is called to create an instance of the class. 
    Calling the constructor publicly is not allowed. The constructor 
    is private and is only called by this Instance function. 
*/ 
CLogger* CLogger::instance(ElogLevel ll) 
{ 
    //cout << "instance run with logLevel = " << (int)ll << endl; 

    // Only allow one instance of class to be generated. 
    if (!mp_instance) 
     mp_instance = new CLogger; 

    mp_instance->m_logLine.logLevel = ll; 
    return mp_instance; 
} 

bool CLogger::startLog(string fileName, bool verbose) 
{ 
    if(remove(fileName.c_str()) != 0) 
     perror("Error deleting file"); 

    m_logFileStream.open(fileName.c_str(), ios::out | ios::app); 
    if (!m_logFileStream.is_open()) 
    { 
     cout << "Could not open log file " << fileName << endl; 
     return false; 
    } 

    m_verbose = verbose; 
    m_logStarted = true; 
    return (pthread_create(&(thread), NULL, threadHelper, this) == 0); 
} 

void CLogger::push_back(logline_t s) 
{ 
    unique_lock<mutex> ul(m_mutex); 
    m_data.emplace_back(move(s)); 
    m_cv.notify_all(); 
} 

CLogger::logline_t CLogger::pop_front() 
{ 
    unique_lock<mutex> ul(m_mutex); 
    m_cv.wait(ul, [this]() { return !m_data.empty(); }); 

    logline_t retVal; 
    retVal.logString = move(m_data.front().logString); 
    retVal.logLevel = move(m_data.front().logLevel); 
    m_data.pop_front(); 

    return retVal; 
} 

void CLogger::setLogLevel(ElogLevel ll) 
{ 
    m_userDefinedLogLevel = ll; 
} 

void CLogger::threadLoop() 
{ 
    logline_t logline; 
    const string logLevelsStrings[] = {"eNone", "eError", "eWarning", "eInfo", "eDebug" }; 

    while(1) 
    { 
     logline = pop_front(); 

     uint32_t pos; 
     if((pos = logline.logString.find('\n')) != string::npos) 
      logline.logString.erase(pos); 

     time_t curTime = time(0); 
     struct tm* now = localtime(&curTime); 

     m_logFileStream << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]" 
       << "[" << logLevelsStrings[(int)logline.logLevel] << "] " 
       << logline.logString << endl; 

     if(m_verbose) 
     { 
      cout << "[" << now->tm_hour << ":" << now->tm_min << ":" << now->tm_sec << "]" 
          << "[" << logLevelsStrings[(int)logline.logLevel] << "] " 
          << logline.logString << endl; 
     } 
    } 
} 
+0

Создайте конечный объект и специализируйте свой 'operator <<' для этого. – 101010

ответ

3

Я предполагаю, что CLogger::instance() возвращает указатель на статическую CLogger объекта. Вы можете изменить его, чтобы вернуть прокси-сервер (по значению), который завершил бы его деструктор. Что-то вроде этого:

struct CLoggerProxy 
{ 
    CLogger &logger; 

    CLoggerProxy(CLogger &logger) : logger(logger) {} 

    ~CLoggerProxy() { logger.finaliseLine(); } 

    template <class T> 
    CLoggerProxy& operator<< (const T &t) const 
    { 
    logger << t; 
    return *this; 
    } 
}; 

CLoggerProxy CLogger::instance() 
{ 
    return CLoggerProxy(*theInstanceYourOriginalCodeReturned()); 
} 
+0

Я пытаюсь реализовать это сейчас, но я не понимаю его полностью. Что вы, конечно, имеете в виду под «logger.finaliseLine();»? Что бы этот метод сделал? – Bremen

+0

@ ŁukaszPrzeniosło Он добавляет '\ n' к logger – KABoissonneault

+2

@Lukasz: Он делает все, что вы хотели, когда новая строка была введена ранее, т. Е. Она сбрасывает регистратор. – Puppy

2

Вы могли бы сделать endl эквивалент:

struct CLoggerEndl_t { }; 
static CLoggerEndl_t CLoggerEndl; // or constexpr, in C++11 

И сказать людям для его использования:

*CLogger::instance(CLogger::ElogLevel::eInfo) 
    << "Application directory " 
    << Enviroment::appDir 
    << CLoggerEndl; 

и обеспечивают особую перегрузку для него:

CLogger& operator<<(CLoggerEndl_t) 
{ 
    push_back(m_logLine); 
    m_logLine.logString.clear(); 

    return *this;   
} 
+0

Так же легко забыть, что и «\ n''. – Angew

+0

Я согласен с @Angew, но это все равно, и я узнал что-то новое. Спасибо . – Bremen

+0

@Angew True, но по крайней мере вы не ищете '' \ n''. Я предполагаю, что настоящее решение представляет собой временный стиль RAII. Каков ваш ответ, поэтому я вернул свое редактирование. – Barry

Смежные вопросы