2017-01-04 2 views
0

Ich habe eine Loggerklasse QueuedLog, die Protokollnachrichten in eine Warteschlange stellt und alle Protokollnachrichten in die Warteschlange in eine std::ostream, falls und wenn gewünscht, einfügt. Um jede Protokollnachricht zu trennen, schrieb ich einen Manipulator mit der Bezeichnung endm, der in ähnlicher Weise wie std::endl verwendet wird. Zum Beispiel, hier ist ein Anwendungsfall:Benutzerdefinierter Manipulator kompiliert mit Visual C++, aber nicht 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 

Mein Code kompiliert mit Visual C++ in Ordnung, aber g ++ und Klirren ++ Kompilierung fehlschlagen, wenn ich versuche, den endm Manipulator zu verwenden. Hier ist eine Minimalversion der QueuedLog (die in der Regel in einer separaten Header-Datei befindet), mit einer kleinen Beispiel-Anwendung demonstriert das Problem:

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

Oder dieses (möglicherweise Über-) vereinfachtes Beispiel:

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

int main() { 
    endm; 
} 

Ich habe versucht, es mit allen drei Compilern um rextester zu kompilieren, aber es kompiliert nur erfolgreich mit Visual C++.

g ++ wird folgende Fehler gibt:

error: ‘endm’ was not declared in this scope

Die Fehlermeldung von Klirren ++ ist ähnlich:

error: use of undeclared identifier 'endm'

Warum funktioniert das in Visual C++, aber nicht g ++ oder Klirren ++? Wie repariere ich es für g ++/clang ++? Die Lösung muss nicht in allen drei Compilern gleichzeitig funktionieren, ich möchte nur wissen, wie man sie für g ++ und clang ++ korrigiert.

+0

Eine viel kürzer [MCVE] ist [hier] verfügbar (http://ideone.com/44UWla). –

+0

@ Robᵩ ideone.com ist bei der Arbeit für mich blockiert. Fühlen Sie sich frei, Ihren MCVE in meine Frage zu bearbeiten. – Null

+1

Mögliches Duplikat: http://stackoverflow.com/questions/23540764/friend-function-is-not-visible-in-the-class –

Antwort

2

In einer Klassendefinition definierte Friend-Funktionen sind nur innerhalb dieser Klassendefinition sichtbar. Sie müssen die Funktion außerhalb der Klassendefinition deklarieren. Fügen Sie diese Zeile außerhalb Ihres class QueuedLog, aber in Ihrem namespace Logger:

extern QueuedLog& endm(QueuedLog&); 
Verwandte Themen