2009-02-10 19 views
6

Je souhaite utiliser cet extrait de Mr-Edd's iostreams article pour imprimer quelque part std :: clog.rediriger std :: cout vers un écrivain personnalisé

#include <iostream> 
#include <iomanip> 
#include <string> 
#include <sstream> 

int main() 
{ 
    std::ostringstream oss; 

    // Make clog use the buffer from oss 
    std::streambuf *former_buff = 
     std::clog.rdbuf(oss.rdbuf()); 

    std::clog << "This will appear in oss!" << std::flush; 

    std::cout << oss.str() << '\\n'; 

    // Give clog back its previous buffer 
    std::clog.rdbuf(former_buff); 

    return 0; 
} 

donc, dans un mainloop, je ferai quelque chose comme

while (! oss.eof()) 
{ 
    //add to window text somewhere 
} 

Voici le ostringstream docs mais je vais avoir du mal à comprendre la meilleure façon de le faire. J'ai une méthode qui affiche le texte, je veux juste l'appeler avec toutes les données dans l'autrutream.

Quel est le moyen le plus simple/le meilleur pour envoyer n'importe quoi à std :: clog redirigé vers une méthode de mon choix? est-ce comme ci-dessus, et remplissez le moment! eof partie (pas sûr comment), ou y a-t-il un meilleur moyen, disons en surchargeant un opérateur 'commit' quelque part qui appelle ma méthode? Je bloque pour rapide et facile, je ne veux vraiment pas commencer à définir des puits et autres avec des iostreams boost comme le fait l'article - ce truc est bien au-dessus de ma tête.

+0

pourrait vous être plus clair sur ce que votre question est? –

Répondre

11

Je vous encourage à regarder Boost.IOStreams. Il semble s'adapter à votre cas d'utilisation bien, et l'utiliser est étonnamment simple:

#include <boost/iostreams/concepts.hpp> 
#include <boost/iostreams/stream_buffer.hpp> 
#include <iostream> 

namespace bio = boost::iostreams; 

class MySink : public bio::sink 
{ 
public: 
    std::streamsize write(const char* s, std::streamsize n) 
    { 
     //Do whatever you want with s 
     //... 
     return n; 
    } 
}; 

int main() 
{ 
    bio::stream_buffer<MySink> sb; 
    sb.open(MySink()); 
    std::streambuf * oldbuf = std::clog.rdbuf(&sb); 
    std::clog << "hello, world" << std::endl; 
    std::clog.rdbuf(oldbuf); 
    return 0; 
} 
+0

votre extrait fonctionne dans un projet vide. J'ai copié-collé votre récepteur dans mon vrai projet et j'ai copié votre main dans une méthode init quelque part, changé RIEN, et j'ai obtenu plusieurs pages d'erreurs non-sens. –

+0

(sur cette ligne: bio :: stream_buffer sb;) –

+0

Je serais ravi de vous aider, mais j'aurais besoin de plus d'informations sur les erreurs que vous voyez. La limite de taille draconienne sur les commentaires ne le rend pas pratique ici, donc je vous suggère de poster votre code et les erreurs qu'il génère sur la liste des utilisateurs de Boost. http://www.boost.org/community/groups.html#users –

7

I pense que vous voulez tirer le texte de l'ostream alors qu'il n'est pas vide. Vous pourriez faire quelque chose comme ceci:

std::string s = oss.str(); 
if(!s.empty()) { 
    // output s here 
    oss.str(""); // set oss to contain the empty string 
} 

Faites-moi savoir si ce n'est pas ce que vous vouliez.

Bien sûr, la meilleure solution est de supprimer l'intermédiaire et d'avoir un nouveau streambuf où vous voulez vraiment, vous n'avez pas besoin de sonder plus tard. quelque chose comme ça (note, ce qu'il fait pour chaque char, mais il y a beaucoup d'options de mise en mémoire tampon streambufs ainsi):

class outbuf : public std::streambuf { 
public: 
    outbuf() { 
     // no buffering, overflow on every char 
     setp(0, 0); 
    } 

    virtual int_type overflow(int_type c = traits_type::eof()) { 
     // add the char to wherever you want it, for example: 
     // DebugConsole.setText(DebugControl.text() + c); 
     return c; 
    } 
}; 

int main() { 
    // set std::cout to use my custom streambuf 
    outbuf ob; 
    std::streambuf *sb = std::cout.rdbuf(&ob); 

    // do some work here 

    // make sure to restore the original so we don't get a crash on close! 
    std::cout.rdbuf(sb); 
    return 0; 

}

+0

Je ne m'attendais pas à être aussi simple que ça, mais je vais essayer. –

0

Si vous voulez juste pour obtenir le contenu du ostringstream, alors utilisez son membre str(). Par exemple:

string s = oss.str();  
4

que je devais saisir les sorties à std :: et std :: Cout cerr des bibliothèques de tiers et de les connecter en utilisant log4cxx et toujours en conservant les sorties d'origine.

Voici ce que j'ai trouvé. Il est assez simple:

  • je remplace l'ancien tampon d'un ostream (comme std :: Cout) avec ma propre classe pour que j'avoir accès à ce qui est jamais écrit.

  • Je crée également un nouvel objet std :: ostream avec l'ancien tampon afin que je puisse continuer à obtenir la sortie sur ma console, en plus de l'envoyer à mon enregistreur. Ce que je trouve un peu pratique.

code:

class intercept_stream : public std::streambuf{ 
public: 
    intercept_stream(std::ostream& stream, char const* logger): 
     _logger(log4cxx::Logger::getLogger(logger)), 
     _orgstream(stream), 
     _newstream(NULL) 
    { 
     //Swap the the old buffer in ostream with this buffer. 
     _orgbuf=_orgstream.rdbuf(this); 
     //Create a new ostream that we set the old buffer in 
     boost::scoped_ptr<std::ostream> os(new std::ostream(_orgbuf)); 
     _newstream.swap(os); 
    } 
    ~intercept_stream(){ 
     _orgstream.rdbuf(_orgbuf);//Restore old buffer 
    } 
protected: 
    virtual streamsize xsputn(const char *msg, streamsize count){ 
     //Output to new stream with old buffer (to e.g. screen [std::cout]) 
     _newstream->write(msg, count); 
     //Output to log4cxx logger 
     std::string s(msg,count); 
     if (_logger->isInfoEnabled()) { 
      _logger->forcedLog(::log4cxx::Level::getInfo(), s, LOG4CXX_LOCATION); 
     } 
     return count; 
    } 
private: 
    log4cxx::LoggerPtr _logger; 
    std::streambuf* _orgbuf; 
    std::ostream&  _orgstream; 
    boost::scoped_ptr<std::ostream> _newstream; 
}; 

ensuite l'utiliser:

std::cout << "This will just go to my console"<<std::endl; 
intercept_stream* intercepter = new intercept_stream(std::cout, "cout"); 
std::cout << "This will end up in both console and my log4cxx logfile, yay!" << std::endl; 
+1

Pourrait améliorer certaines performances en déplaçant la construction std :: string dans la vérification isInfoEnabled. –

+0

Cela fonctionne très bien! J'ai seulement changé intercept_stream pour prendre une chaîne std :: au lieu de char const * puisque getLogger prend une chaîne et a déplacé la construction de la chaîne à l'intérieur de la commande isInfoEnabled selon la suggestion de Rhys Ulerich. – Fred

1

Pour l'exemple log4cxx vous devez remplacer le débordement() et de synchronisation() sinon le badbit est toujours réglé après la première flux est reçu.

Voir: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/fd9d973282e0a402/a872eaedb142debc

InterceptStream::int_type InterceptStream::overflow(int_type c) 
{ 
    if(!traits_type::eq_int_type(c, traits_type::eof())) 
    { 
     char_type const t = traits_type::to_char_type(c); 
     this->xsputn(&t, 1); 
    } 
    return !traits_type::eof(); 
} 

int InterceptStream::sync() 
{ 
    return 0; 
}