如何定义运算符的结尾<<方法?

时间:2015-06-26 13:34:54

标签: c++ logging stream singleton cout

感谢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;
        }
    }
}

2 个答案:

答案 0 :(得分: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());
}

答案 1 :(得分:2)

你可以制作map = new UnfoldingMap(this, new Google.GoogleMapProvider()); 等价物:

endl

并告诉人们使用它:

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

并为它提供特殊的重载:

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