2015-06-26 27 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; 
    } 

我的問題是,我不知道什麼時候做了一個實例的輸入聚會(一個方法運行)完成。正如你可以在代碼中看到的那樣,我有一些想法,因爲它是我能想到的唯一的方法 - 該方法將完成收集用戶輸入併發送打印數據(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

創建結束對象,並專注你的'運營商<<'它。 – 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' – 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真的,但至少你不是在尋找''\ n''。我想真正的解決方案是引入臨時RAII風格。這是你的答案,所以我恢復了我的編輯。 – Barry