2017-01-04 6 views
0

ログメッセージをキューに入れ、必要に応じてキュー内のすべてのログメッセージをstd::ostreamに挿入するロガークラスQueuedLogがあります。各ログメッセージを分離するために、endmというマニピュレータを作成しました。これはstd::endlと同様の方法で使用されます。例えば、ここではユースケースです:カスタムマニピュレータはVisual C++でコンパイルされますが、g ++/clangではコンパイルされません

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 ++および打ち鳴らす++のコンパイルに失敗。ここで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で3つのコンパイラをすべてコンパイルしようとしましたが、Visual C++でしかコンパイルできません。次のエラー与え++

グラム:++打ち鳴らすから

error: ‘endm’ was not declared in this scope

エラーメッセージは似ています

error: use of undeclared identifier 'endm'

なぜではなく、G ++または打ち鳴らす++ Visual C++で、この作品?どうすればg ++/clang ++のために修正できますか?解決策は、3つのコンパイラで同時に動作する必要はありません。g ++とclang ++の問題を修正する方法を知りたいだけです。

+0

[MCVE]はるかに短い(http://ideone.com/44UWla)[ここ]使用可能です。 –

+0

@Robᵩideone.comは私の仕事ではブロックされています。あなたのMCVEを私の質問に自由に編集してください。 – Null

+1

可能な重複:http://stackoverflow.com/questions/23540764/friend-function-is-not-visible-in-the-class –

答えて

2

クラス定義内で定義されたフレンド関数は、そのクラス定義内でのみ表示されます。クラス定義の外で関数を宣言する必要があります。あなたのclass QueuedLog外にこの行を追加しますが、あなたのnamespace Logger内側:

extern QueuedLog& endm(QueuedLog&); 
関連する問題