3

The easylogging++ code defines a macro that makes it very simple to use:

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

I want to make a wrapper class for easylogging++. I can easily create a function with two parameters to wrap the above line. However, is it possible to mimic this syntax in a wrapper class? For example:

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

I know I can easily overload the insertion operator, but that still leaves me with having to write another function to set the log level each time.

UPDATE:

Thanks to Starl1ght's answer I was able to get this working. I figured I would share in case anyone else has a similar need.

I created two overloads. One was for () and the other for <<.

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


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

UPDATE 2:

I wanted to update this post again to give my reasoning and show my final solution.

My reasoning is that my project is a demonstration of abstraction. I'm trying to demonstrate that logging libraries (and many other things) can be abstracted away from the core functionality of your software. This also makes the software components modular. This way I can swap out the easylogging++ library without loosing the syntax because it is implemented in the module interface.

My last update didn't mention how I overcame the obstacle of insertion chaining, so I wanted to post an example to show how I did it. The following code is a simplified example of how to achieve std::cout like syntax for a class.

#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;
}

I feel like I could have done a better job with the naming and the comments but I am pressed for time. I'm definitely open to any comments or critiques.

4

1 回答 1

3

您必须重载operator(),它将设置内部日志级别并返回*this为 type Logger&,因此,重载operator<<将对返回的引用起作用,并设置必要的日志级别。

像这样的东西:

Logger& Logger::operator()(LogLevel level) {
    // set internal log level
    return *this;
}
于 2016-11-30T18:02:02.363 回答