2009-03-23 9 views
10

Comment puis-je dériver une classe de cout de sorte que, par exemple, écrit à cecustomize Cout

new_cout << "message";

équivaudrait à

cout << __FUNCTION__ << "message" << "end of message" << endl;

Répondre

29
class Log 
{ 
public: 
    Log(const std::string &funcName) 
    { 
     std::cout << funcName << ": "; 
    } 

    template <class T> 
    Log &operator<<(const T &v) 
    { 
     std::cout << v; 
     return *this; 
    } 

    ~Log() 
    { 
     std::cout << " [end of message]" << std::endl; 
    } 
}; 

#define MAGIC_LOG Log(__FUNCTION__) 

Par conséquent:

MAGIC_LOG << "here's a message"; 
MAGIC_LOG << "here's one with a number: " << 5; 
+0

+1. Contrairement à toutes les réponses qui disent simplement "override operator <<()", cette solution répond au fait que l'en-tête et le pied de page doivent être sortis une fois par instruction, pas une fois par <<. –

+0

+1 en effet, bonne idée. juste quelque chose à propos de endl: je suppose que pour un message de débogage, endl est rarement nécessaire, mais si vous voulez pouvoir le mettre dans le journal magique aussi, mettez un Log & operator << (ostream & (* f) (ostream &)) { cout << * f; retour * ceci; } –

+0

@Earwicker. Merci pour votre belle solution. S'il vous plaît, expliquez ce que cela signifie d'instancier une classe sans créer de variable objet. Je suis nouveau en C++ et je n'ai jamais vu ça auparavant. – jackhab

-1

Vous pouvez également override the operator. Il vous permettra d'appeler une autre fonction ou préfixe/suffixe tout ce qui va laisser le tampon de sortie avec ce que vous souhaitez: Dans votre cas, vous auriez sorti une chaîne spécifique.

+0

-1.Le remplacement de l'opérateur produira un en-tête et un pied de page pour chaque élément formaté: par ex. "new_cout << 1 << 2 << 3;" produira 3 en-têtes et pieds de page. –

+0

Oui, mes suggestions supposent que la personne qui écrit l'override sache où cela doit être fait, pour quels types, et que son code va effectivement l'utiliser correctement. –

0

Vous devez remplacer l'opérateur < <(), mais vous n'avez même pas besoin de sous-classer std :: cout. Vous pouvez également créer un nouvel objet ou utiliser des objets existants comme ça.

1

plus de la réponse de Mykola, j'ai la mise en œuvre suivante dans mon code. L'utilisation est

  LOG_DEBUG("print 3 " << 3); 

impressions

  DEBUG (f.cpp, 101): print 3 3 

Vous pouvez le modifier pour utiliser FONCTION le long/à la place de LIGNE et FICHIER

/// Implements a simple logging facility. 
class Logger 
{ 
     std::ostringstream os_; 
     static Logger* instance_; 
     Logger(); 
public: 
     static Logger* getLogger(); 
     bool isDebugEnabled() const; 
     void log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const; 
     std::ostringstream& getStream() 
     { return os_; } 
}; 

void Logger::log(LogLevelEnum l, std::ostringstream& os, const char* filename, int lineno) const 
{ 
     std::cout << logLevelEnumToString(l) << "\t(" << fileName << ": " << lineno << ")\t- " << os.str(); 
     os.str(""); 
} 

#define LOG_common(level, cptext) do {\ 
     utility::Logger::getLogger()->getStream() << cptext; \ 
     utility::Logger::getLogger()->log(utility::level, utility::Logger::getLogger()->getStream(), __FILE__, __LINE__); \ 
} while(0); 

enum LogLevelEnum { 
     DEBUG_LOG_LEVEL, 
     INFO_LOG_LEVEL, 
     WARN_LOG_LEVEL, 
     ERROR_LOG_LEVEL, 
     NOTICE_LOG_LEVEL, 
     FATAL_LOG_LEVEL 
}; 

#define LOG_DEBUG(cptext) LOG_common(DEBUG_LOG_LEVEL, cptext) 
#define LOG_INFO(cptext)  LOG_common(INFO_LOG_LEVEL , cptext) 
#define LOG_WARN(cptext)  LOG_common(WARN_LOG_LEVEL , cptext) 
#define LOG_ERROR(cptext) LOG_common(ERROR_LOG_LEVEL, cptext) 
#define LOG_NOTICE(cptext) LOG_common(NOTICE_LOG_LEVEL, cptext) 
#define LOG_FATAL(cptext) LOG_common(FATAL_LOG_LEVEL, cptext) 

const char* logLevelEnumToString(LogLevelEnum m) 
{ 
     switch(m) 
     { 
       case DEBUG_LOG_LEVEL: 
         return "DEBUG"; 
       case INFO_LOG_LEVEL: 
         return "INFO"; 
       case WARN_LOG_LEVEL: 
         return "WARN"; 
       case NOTICE_LOG_LEVEL: 
         return "NOTICE"; 
       case ERROR_LOG_LEVEL: 
         return "ERROR"; 
       case FATAL_LOG_LEVEL: 
         return "FATAL"; 
       default: 
         CP_MSG_ASSERT(false, CP_TEXT("invalid value of LogLevelEnum")); 
         return 0; 
     } 
} 
2
#define debug_print(message) (std::cout << __FUNCTION__ << (message) << std::endl) 

Ceci a l'avantage que vous pouvez désactiver tous les messages de débogage à la fois lorsque vous avez terminé

#define debug_print(message)() 
0

Aux fins de l'exploitation forestière j'utiliser quelque chose comme

#define LOG(x) \ 
    cout << __FUNCTION__ << x << endl 

// ... 
LOG("My message with number " << number << " and some more"); 

Le problème avec votre approche est (comme Mykola Golybyew a expliqué) que FONCTION est traitée au moment de la compilation et imprimerait donc toujours le même nom avec une solution sans préprocesseur.

Si c'est seulement pour ajouter endl à vos messages, vous pouvez essayer quelque chose comme:

class MyLine { 
public: 
    bool written; 
    std::ostream& stream; 
    MyLine(const MyLine& _line) : stream(_line.stream), written(false) { } 
    MyLine(std::ostream& _stream) : stream(_stream), written(false) { } 
    ~MyLine() { if (!written) stream << "End of Message" << std::endl; } 
}; 

template <class T> MyLine operator<<(MyLine& line, const T& _val) { 
    line.stream << _val; 
    line.written = true; 
    return line; 
} 

class MyStream { 
public: 
    std::ostream& parentStream; 
    MyStream(std::ostream& _parentStream) : parentStream(_parentStream) { } 
    MyLine getLine() { return MyLine(parentStream); } 
}; 

template <class T> MyLine operator<<(MyStream& stream, const T& _val) { 
    return (stream.getLine() << _val); 
} 

int main() 
{ 
     MyStream stream(std::cout); 
     stream << "Hello " << 13 << " some more data"; 
     stream << "This is in the next line " << " 1 "; 
    return 0; 
} 

Notez qu'il est important de ne pas retourner les références des fonctions de l'opérateur. Puisque le MyLine ne devrait exister que temporairement (car son destructeur déclenche l'écriture du endl), le premier objet (retourné par la fonction getLine() dans MyStream) serait détruit avant que le second operator<< ne soit appelé. Par conséquent, l'objet MyLine est copié dans chaque operator<< pour en créer un nouveau. Le dernier objet est détruit sans être écrit et écrit la fin du message dans son destructeur. Essayez-le dans le débogueur pour comprendre ce qui se passe ...

Questions connexes