自定义操纵器使用Visual C ++编译,但不编译g ++ / clang

时间:2017-01-04 22:55:41

标签: c++ visual-c++ g++ clang++ manipulators

我有一个记录器类QueuedLog,它将日志消息排队,并在需要时将队列中的所有日志消息插入std::ostream。为了分隔每条日志消息,我编写了一个名为endm的操纵器,其使用方式与std::endl类似。例如,这是一个用例:

QueuedLog log(INFO);

// do stuff

log << "test: 0x" << std::hex << std::uppercase << 15 << endm; // add a log message

// do more stuff

log << "log something else" << endm;

std::cout << log << std::endl; // insert all the queued up log messages into cout

// do more stuff

log << "start another message...";

// calculate something, add it to a log message and end the message
log << std::dec << 42 << endm;

std::cout << log << std::endl; // insert all the queued up log messages into cout

log << "add yet another message" << endm;

// don't need to log that last message after all
// leave it in the QueuedLog instead of inserting it into cout

我的代码使用Visual C ++编译得很好但是当我尝试使用endm操纵器时,g ++和clang ++编译失败。这是QueuedLog的最小版本(通常位于单独的头文件中),其中一个示例用法演示了该问题:

#include <ios>
#include <iostream>
#include <string>
#include <sstream>
#include <deque>
#include <stdexcept>

namespace Logger {

enum LogType {
    NONE,   
    DEBUG,  
    INFO,   
    WARN,   
    ERROR,  
    FATAL   
};

// Converts a LogType to a `std::string` which can be prepended to a log message.
std::string prepend_type(LogType type) {
    switch (type) {
        case DEBUG: return std::string("[DEBUG] ");
        case INFO: return std::string("[INFO] ");
        case WARN: return std::string("[WARN] ");
        case ERROR: return std::string("[ERROR] ");
        case FATAL: return std::string("[FATAL] ");
        default: return std::string("");
    }
}

class QueuedLog {
    /* Holds a partially contructed log message.

    A std::stringstream is used instead of a std::string so that non-string data types can be inserted into
    the QueuedLog without requiring conversion. Also, client code can apply I/O manipulators like std::hex.
    */
    std::ostringstream stream;

    std::deque<std::string> messages; // Holds the queued, completed log message(s).

    // The LogType of new messages inserted into the QueuedLog. This can be changed at any time.
    LogType log_type;
public:
    QueuedLog(LogType logtype = NONE) : log_type(logtype) {} // Constructs a QueuedLog with no text and an initial LogType.

    // Inserts a character sequence into the QueuedLog.
    template<typename T> inline QueuedLog& operator<<(const T& message) {
    //inline QueuedLog& operator<<(const std::string& message) { // non-template version doesn't work, either
        // Only prepend with logtype if it is the beginning of the message
        if (stream.str().empty()) stream << prepend_type(log_type);

        stream << message;
        return *this;
    }

    // Overload used for manipulators like QueuedLog::endm()
    inline QueuedLog& operator<<(QueuedLog& (*pf)(QueuedLog&)) {
        (*pf)(*this);
        return *this;
    }

    // Adds the newline character and marks the end of a log message.
    friend inline QueuedLog& endm(QueuedLog& log) {
        log.stream << log.stream.widen('\n');

        // Add the completed message to the messages deque, and reset the stream for the next message
        log.messages.push_back(log.stream.str());
        log.stream.str(""); // clear the underlying string
        log.stream.clear(); // clear any error flags on the stream

        return log;
    }

    /* Inserts all the completed log messages in the QueuedLog object into a std::ostream.

    If the QueuedLog contains an incomplete log message (a message that has not been terminated by QueuedLog::endm())
    then that partial message will not be inserted into the std::ostream.
    */
    friend inline std::ostream& operator<<(std::ostream& os, QueuedLog& log) {
        while (!log.messages.empty()) {
            os << log.messages.front();
            log.messages.pop_front();
        }

        return os;
    }
};

} // end namespace Logger

using namespace Logger;

int main() {
    QueuedLog log(INFO);

    log << "test: 0x" << std::hex << std::uppercase << 15; // compiles by itself with all compilers
    log << endm; // but compilation error w/ g++/clang++ when trying to use endm
    std::cout << log << std::endl;
}

或者,这个(可能超过)简化的例子:

class QueuedLog {
public:
    friend inline void endm() {
    }
};

int main() {
    endm;
}

我试图用rextester的所有三个编译器编译它,但它只能用Visual C ++成功编译。

g ++给出了以下错误:

  

error: ‘endm’ was not declared in this scope

来自clang ++的错误消息类似于:

  

error: use of undeclared identifier 'endm'

为什么这在Visual C ++中有效,而不是g ++或clang ++?我如何修复g ++ / clang ++?该解决方案不需要同时在所有三个编译器中工作,我只想知道如何为g ++和clang ++修复它。

1 个答案:

答案 0 :(得分:2)

在类定义中定义的

友元函数仅在该类定义中可见。您需要在类定义之外声明该函数。将此行添加到class QueuedLog之外,但在namespace Logger

extern QueuedLog& endm(QueuedLog&);