是否可以通过与 easylogging++ 相同的方式链接重载插入运算符来创建一个模仿 std::cout 语法的 C++ 类?

Posted

技术标签:

【中文标题】是否可以通过与 easylogging++ 相同的方式链接重载插入运算符来创建一个模仿 std::cout 语法的 C++ 类?【英文标题】:Is it possible to create a C++ class that mimics the std::cout syntax by chaining overloaded insertion operators the same way that easylogging++ does? 【发布时间】:2016-11-30 17:59:32 【问题描述】:

easylogging++ 代码定义了一个宏,使它非常易于使用:

LOG(logLevel) << "This mimics std::cout syntax.  " << 1 << " + " << 1 << " = " << 2;

我想为 easylogging++ 创建一个包装类。我可以轻松地创建一个带有两个参数的函数来包装上面的行。但是,是否可以在包装类中模仿这种语法?例如:

Logger logger;
logger(logLevel) << "Line " << 1 << " of log text.";

我知道我可以轻松地重载插入运算符,但这仍然让我不得不编写另一个函数来每次设置日志级别。

更新:

感谢 Starl1ght 的回答,我得以完成这项工作。我想我会分享,以防其他人有类似的需求。

我创建了两个重载。一个用于 (),另一个用于

Logger &operator()(logLevelT logLevel) 
  mLogLevel = logLevel;
  return *this;



template <typename T>
Logger &operator<<(T const &value) 
  LOG(mLogLevel) << value;
  return *this;

更新 2:

我想再次更新这篇文章以给出我的推理并展示我的最终解决方案。

我的理由是我的项目是抽象的演示。我试图证明日志库(和许多其他东西)可以从您的软件的核心功能中抽象出来。这也使得软件组件模块化。这样我就可以在不丢失语法的情况下更换 easylogging++ 库,因为它是在模块接口中实现的。

我上次的更新没有提到我是如何克服插入链的障碍的,所以我想发布一个示例来展示我是如何做到的。以下代码是一个简化示例,说明如何为类实现类似 std::cout 的语法。

#include <iostream>         // For cout
#include <string>           // For strings
#include <sstream>          // For ostringstream


enum logLevelT 
    INFO_LEVEL,
    WARNING_LEVEL,
    ERROR_LEVEL,
    FATAL_LEVEL
;


class Logger 
private:
    std::string logName;

public:
    Logger(std::string nameOfLog, std::string pathToLogFile) 
        logName = nameOfLog;

        //TODO Configure your logging library and instantiate
        //     an instance if applicable.
    


    ~Logger()


    // LogInputStream is instantiated as a temporary object.  It is used
    //  to build the log entry stream.  It writes the completed stream
    //  in the destructor as the object goes out of scope automatically.
    struct LogInputStream 
        LogInputStream(logLevelT logLevel, std::string nameOfLog) 
            currentLogLevel = logLevel;
            currentLogName = nameOfLog;
        


        // Copy Constructor
        LogInputStream(LogInputStream &lis) 
            currentLogLevel = lis.currentLogLevel;
            currentLogName = lis.currentLogName;
            logEntryStream.str(lis.logEntryStream.str());
        


        // Destructor that writes the log entry stream to the log as the
        //  LogInputStream object goes out of scope.
        ~LogInputStream() 
            std::cout << "Logger: " << currentLogName
                      << "   Level: " << currentLogLevel
                      << "   logEntryStream = " << logEntryStream.str()
                      << std::endl;

            //TODO Make a log call to your logging library.  You have your log level
            //     and a completed log entry stream.
        


        // Overloaded insertion operator that adds the given parameter
        //  to the log entry stream.
        template <typename T>
        LogInputStream &operator<<(T const &value) 
            logEntryStream << value;
            return *this;
        


        std::string currentLogName;
        logLevelT currentLogLevel;
        std::ostringstream logEntryStream;
    ;


    // Overloaded function call operator for providing the log level
    Logger::LogInputStream operator()(logLevelT logLevel) 
        LogInputStream logInputStream(logLevel, logName);

        return logInputStream;
    


    // Overloaded insertion operator that is used if the overloaded
    //  function call operator is not used.
    template <typename T>
    Logger::LogInputStream operator<<(T const &value) 
        LogInputStream logInputStream(INFO_LEVEL, logName);

        logInputStream << value;

        return logInputStream;
    
;



int main(int argc, char *argv[]) 

    Logger logger1 = Logger("Logger1", "/path/to/log.log");
    Logger logger2 = Logger("Logger2", "/path/to/log.log");

    logger1(INFO_LEVEL) << "This is the " << 1 << "st test";

    logger2(ERROR_LEVEL) << "This is the " << 2 << "nd test";

    logger2 << "This is the " << 3 << "rd test";

    return 0;

我觉得我可以在命名和 cmets 方面做得更好,但我时间紧迫。我绝对愿意接受任何 cmets 或批评。

【问题讨论】:

它已经很好地实现了。您编写更多代码的原因很简单! 【参考方案1】:

您必须重载operator(),因此,它将设置内部日志级别并将*this 作为类型Logger&amp; 返回,因此,重载operator&lt;&lt; 将在返回的引用上工作,并设置必要的日志级别。

类似这样的:

Logger& Logger::operator()(LogLevel level) 
    // set internal log level
    return *this;

【讨论】:

非常感谢!这正是让它发挥作用所需要的。

以上是关于是否可以通过与 easylogging++ 相同的方式链接重载插入运算符来创建一个模仿 std::cout 语法的 C++ 类?的主要内容,如果未能解决你的问题,请参考以下文章

easylogging++:在应用程序启动时清除日志文件

cmake:下载easylogging++,直接使用源码

easylogging++ 如何避免多次初始化

easylogging++学习记录:流式日志

GLSDK 打破了easylogging++

使用 easylogging++ 记录 QString 时插入的额外空间