2010-11-12 26 views
3

J'ai lu des tonnes de questions, d'articles et de documentation, mais je n'ai pas trouvé de solution à mon problème.Classe de journalisation wostream simple (avec des manipulateurs de flux personnalisés)

Je voudrais créer une classe simple à utiliser pour le débogage. Le résultat final de ce qui me permettrait de faire quelque chose comme ceci:

logger << error << L"This is a problem!" << endl; 
logger << warning << L"This might be a problem!" << endl; 
logger << info << L"This isn't a problem but I thought you should know about it" << endl; 

Avec l'idée que, dans la classe de l'enregistreur que je peux basculer si oui ou non ces choses font à la console/fichier de débogage. J'ai un squelette ensemble, mais je n'arrive pas à obtenir une surcharge de l'opérateur pour que les manipulateurs fonctionnent correctement.

Voilà Logger.h:

class LoggerBuffer : public wfilebuf { 
// Functions 
    public: 
     LoggerBuffer() { wfilebuf::open("NUL", ios::out); currentState = 1;} 
     ~LoggerBuffer() {wcout << "DELETED!" << endl;} 
     void open(const char fname[]); 
     void close() {wfilebuf::close();} 
     virtual int sync(); 
     void setState(int newState); 
// Variables 
    private: 
     int currentState; 
}; 

class LoggerStream : public wostream { 
// Functions 
    public: 
     LoggerStream() : wostream(new LoggerBuffer()), wios(0) {} 
     ~LoggerStream() { delete rdbuf(); } 
     void open(const char fname[] = 0) { 
    wcout << "Stream Opening " << fname << endl;((LoggerBuffer*)rdbuf())->open(fname); } 
     void close() { ((LoggerBuffer*)rdbuf())->close(); } 
     void setState(int newState); 
}; 

Et Logger.cpp:

void LoggerBuffer::open(const char fname[]) { 
    wcout << "Buffer Opening " << fname << endl; 
    close(); 
    wfilebuf* temp = wfilebuf::open(fname, ios::out); 
    wcout << "Temp: " << temp << endl; 
} 
int LoggerBuffer::sync() { 
    wcout << "Current State: " << currentState << ", Data: " << pbase(); 
    return wfilebuf::sync(); 
} 
void LoggerBuffer::setState(int newState) { 
    wcout << "New buffer state = " << newState << endl; 
    currentState = newState; 
} 

void LoggerStream::setState(int newState) { 
    wcout << "New stream state = " << newState << endl; 
    ((LoggerBuffer*)rdbuf())->setState(newState); 
} 

Et main.cpp:

struct doSetState { 
    int _l;  
    doSetState (int l): _l (l) {} 

    friend LoggerStream& operator<< (LoggerStream& os, doSetState fb) { 
     os.setState(3); 
     return (os); 
    } 
}; 

... 
LoggerStream test; 
test.open("debug.txt"); 
test << "Setting state!" << doSetState(1) << endl; 
... 

Ce gâchis produit l'erreur suivante dans VS2005:

"erreur C2679: binaire '< <': aucun opérateur trouvée, ce qui prend un opérande à droite de type 'doSetState' (ou il n'y a pas conversion acceptable)"

Toute aide est FORTEMENT apprécié.

Merci!

Répondre

0

Votre opérateur ostream n'a pas la bonne signature. Il devrait être:

friend LoggerStream& operator<< (LoggerStream& os, const doSetState& fb)

(. Il est nécessaire d'utiliser une référence, car un compilateur simple passe ne connaît pas la taille de doSetState quand il est à mi-chemin à travers la définition de la classe)

+0

J'apprécie votre aide mais même avec ce changement j'obtenir exactement la même erreur. – RotsiserMho

0

Le le problème est que lorsque vous faites cela:

test << "Setting state!" 

Il renvoie un objet wostream de base. enchaînant donc il ne fonctionne pas, car il n'y a pas de surcharge pour:

wostream& operator<< (wostream& os, const doSetState& fb) 

Vous pouvez cependant le faire sur des lignes distinctes, comme ceci:

test << "Setting state!"; 
test << doSetState(1) << endl; 
+0

Malheureusement, cela va être plutôt désagréable. Je cherche vraiment comment implémenter quelque chose comme "endl" qui s'enchaîne dans le flux. Qu'est-ce que je devrais faire pour que cela fonctionne? Puis-je faire quelque chose comme ça? 'wostream & opérateur << (LoggerStream & os, const doSetState & fb)' – RotsiserMho

+0

@RotsiserMho: Pour être honnête, je ne suis pas sûr. Je n'ai jamais essayé d'hériter des cours d'iostream comme vous le faites, donc je ne connais pas tous les tenants et les aboutissants. Je sais écrire un manipulateur général qui peut agir sur n'importe quel ostream. Mais vous essayez d'appeler une fonction spécifique à LoggerStream, de sorte que cette approche ne fonctionne pas. Je vais devoir y regarder un peu plus. En attendant, vous devriez peut-être faire un autre post pour poser des questions sur ce problème spécifique, de telle sorte que plus de gens le verront probablement. –

0

Je pencherais pour une approche légèrement différente.

Au lieu d'hériter de std::wostream, j'aurais un membre std::wfostream dans ma classe d'enregistreur. Ensuite, vous pouvez avoir un modèle générique operator<< qui redirige sélectivement vers le flux intégré.

Par exemple:

class Logger; 

template<class T> Logger& operator<<(Logger&, const T&); 

enum LogLevel 
{ 
    debug, 
    info, 
    warning, 
    error 
}; 

class Logger 
{ 
public: 
    void open(const char* file) { stream.open(file); } 
    void close() { stream.close(); } 
    void passLevel(Loglevel level) { pass = level; } 
    void logLevel(LogLevel level) { current = level; } 
private: 
    bool passThrough() { return current >= pass; } 

    std::wofstream stream; 
    LogLevel pass; 
    LogLevel current; 

    friend template<class T> Logger& operator<<(Logger&, const T&); 
}; 

template<class T> 
Logger& operator<<(Logger& log, const T& rhs) 
{ 
    if (log.passthrough()) 
    { 
     log.stream << rhs; 
    } 
    return log; 
} 

Logger& operator<<(Logger&, LogLevel level) 
{ 
    log.logLevel(level); 
    return log; 
} 

struct setLogLevel { 
    setLogLevel(LogLevel l) : level(l) { } 
    LogLevel level; 
}; 

Logger& operator<<(Logger&, const setLogLevel setter) 
{ 
    log.passLevel(setter.level); 
    return log; 
}