C ++运算符<<超载

时间:2014-05-05 20:46:16

标签: c++ logging operator-overloading

我有一个第三方(日志记录)类,它重载了<<运营商。使用此记录器类的客户端代码可以通过调用其中一个预定义的宏来使用它。举个例子:

//logs can be filtered based on this module id string
LOGGER_INFO("MODULE_ID_STR") << "Logging at info level"; 

我想扩展此功能,其中使用此第三方记录器的类/模块不必每次都包含模块ID字符串。含义 - 客户端代码应该设置模块ID字符串一次,然后才能执行此操作:

cLogger.INFO << "Logging at info level"; 

上述调用应在内部使用先前注册的已注册模块ID字符串,然后使用该字符串进行实际的第三方日志调用。因此,通过重载&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;每个日志级别的运算符。

一些额外的细节......我开始这样做:

这是扩展第三方记录器功能的类:

class LoggerEx
{
public:
    LoggerEx(const std::string &moduleToLog)
    {
       m_ModuleID = moduleToLog;
    };
    virtual ~LoggerEx() {};

    class Debug
    {
      //overload the << operator (how to write this..??)
      LOGGER_INFO(m_ModuleID) << "Logging at info level";
    };

    class Info
    {
      //overload the << operator
    };
    //Note that there could be more such levels
    // (INFO, WARN, ERROR, TRACE, FATAL, etc).

public:
    Debug DEBUG;
    Info  INFO;

protected:
    std::string m_ModuleID

};

应允许使用logger类的一些客户端代码执行此操作...

class Xyz
{
public:
     Xyz() : l("Xyz")
     {}
     void doSomething()
     {
         l.DEBUG << "Doing something"; 
     }
protected:
     Logger l;
};

另一个客户类......

class Mno
{
public:
     Xyz() : l("Mno")
     {}

     void processSomething()
     {
         l.INFO << "Process something"; 
     }
protected:
     Logger l;
};

由于原始记录器支持多种数据类型(int,float,chars,std :: string),以上是方法,还是有任何其他想法/解决方案可以在C ++中更优雅地完成此操作而无需编写完整的吹制包装(或复制代码)到记录器?

谢谢...

1 个答案:

答案 0 :(得分:2)

这实际上比人们想象的更难,主要是因为在典型的日志记录库中,LOGGER_INFO宏或其等价物不仅仅是为您提供流。这是Boost的一个典型宏:

#define BOOST_LOG_STREAM_WITH_PARAMS_INTERNAL(logger, rec_var, params_seq)\
    for (::boost::log::record rec_var = (logger).open_record((BOOST_PP_SEQ_ENUM(params_seq))); !!rec_var;)\
        ::boost::log::aux::make_record_pump((logger), rec_var).stream()

快速查看此代码,可以看到它会创建一个新的record,创建一个pump,从此泵中获取stream,并且您的<< "log text here" << " more log stuff"来电实际运行在那条小溪上。在语句结束时,泵和记录被破坏时,消息实际上被推送到单个日志条目中,这在您想到它时是有意义的 - 您期望LOGGER_INFO(m_ModuleID) << "Logging at info level" << "more text";生成一个日志条目而不是两个。

因此像

这样的天真实现
class LoggerEx
{
public:
    LoggerEx(const std::string &moduleToLog) : Debug(moduleToLog)
    { }
    ~LoggerEx() {}

    class Debug
    {
      private:
          std::string m_ModuleID;
      public:
          Debug(const std::string &module) : m_ModuleID(module) {}
          template <typename T>
          const Debug & operator << (const T& thing_to_log) const {
              LOGGER_INFO(m_ModuleID) << thing_to_log;
              return *this;
          }
    };

public:
    Debug DEBUG;
};
只有在日志记录代码中每个语句只使用<<时,

才有效。

绕过它的一种可能方法是使用内部流来存储日志条目:

class LoggerEx
{
public:
    LoggerEx(const std::string &moduleToLog) : m_module(moduleToLog)
    { }
    ~LoggerEx() {}

    class Debug
    {
      private:
          std::string m_ModuleID;
          std::stringstream m_ss;
      public:
          Debug(const std::string &module) : m_ModuleID(module) {}
          Debug(const Debug &other) : m_ModuleID(other.m_ModuleID) {}
          ~Debug() { 
              std::string str = m_ss.str();
              if(!str.empty())
                  LOGGER_INFO(m_ModuleID) << str;
          }
          template <typename T>
          Debug & operator << (const T& thing_to_log) {
              m_ss << thing_to_log;
              return *this;
          }
    };

public:
    Debug DEBUG() { return Debug(m_module);}
private:
    std::string m_module;
};

它会被称为

l.DEBUG() << "Some stuff " << some_number << " some more stuff";

这个想法是DEBUG()调用产生一个临时对象;您对该临时对象的operator <<次调用会将内容写入stringstream,并且在该行的末尾,当临时对象被破坏时,stringstream中的内容会被推送到记录库。