2009-06-15 4 views
1

Je cherche un moyen de branche (tee) l'entrée lue d'un istream (cin, dans mon cas) sur un fichier journal (sabot/ofstream/etc), tout en utilisant l'entrée pour le traitement.Tee-ing entrée (cin) sur un fichier journal (ou sabot)

J'ai lu à propos de boost :: tee_device, et il est très similaire à mes exigences. Malheureusement, il est implémenté comme un ostream, et résout ainsi un problème similaire de "l'autre côté de la pipe".

J'ai essayé d'écrire une classe istream (adaptateur) qui transmet les fonctions d'entrée à un flux d'entrée enveloppé (cin), et envoie également ce qui a été lu dans le fichier journal. Cela fonctionne très bien pour les types de base qui appellent l'opérateur >> (...) directement, cependant, j'ai rencontré des problèmes avec une utilisation plus avancée du flux d'entrée, par exemple, pour l'opérateur >> (std :: chaîne), et la fonction std :: string getline.

Existe-t-il un moyen plus simple de le faire (éventuellement via la manipulation de rdbuf())?

Merci! Editer: Je pourrais changer mon code partout dans quelque chose comme: Cin >> valeur; boucher < < valeur; - mais ce serait un changement important et laid. Je préférerais aussi avoir un moyen facile de désactiver la journalisation. Ainsi, je voudrais un moyen de modéliser cela en tant que "filtre" istream, puis de simplement remplacer toutes les références à cin avec cet istream "logger".

solution idéale:

class log_istream : public std::istream 
{ 
public: 
    log_istream(std::istream & in , std::ostream & out); 

    /* ... istream forwarding functions ... */ 

private: 
    std::istream & in_; 
    std::ostream & out_;  
}; 

int main() { 
    log_istream logger(std::cin , std::ofstream("logfile.out")); 

    logger >> value; // this implies infile >> value and logfile << value 
    getline(logger,my_string); // this also implies logfile.writeline(value) 
    // etc 
} 

etc.

+0

J'ai essayé de faire ce que vous avez écrit dans votre message ci-dessus à un moment donné, et cela a fonctionné jusqu'à ce que des caractères spéciaux aient été atteints (comme endl), puis la classe a cessé de fonctionner comme prévu. J'ai hâte de voir les réponses ici. –

+0

Seraient-ils aider: [http://stackoverflow.com/questions/999120/c-hello-world-boost-tee-example-program](http://stackoverflow.com/questions/999120/c-hello-world -boost-tee-example-program) [http://stackoverflow.com/questions/670465/using-boostiostreamsteedevice](http://stackoverflow.com/questions/670465/using-boostiostreamsteedevice) – stefanB

Répondre

1

Réponse finale:

#ifndef TEE_ISTREAM_H_ 
#define TEE_ISTREAM_H_ 

/*****************************************************************************/ 

#include <boost/iostreams/tee.hpp> 
#include <boost/iostreams/invert.hpp> 
#include <boost/iostreams/device/file.hpp> 
#include <boost/iostreams/filtering_stream.hpp> 
#include <boost/tr1/memory.hpp> 
#include <iostream> 

/*****************************************************************************/ 

namespace bio = boost::iostreams; 

/*****************************************************************************/ 

class tee_source : public bio::source { 
public: 
    tee_source(std::istream & in, const std::string & filename) 
     : in_(in), log_file_(filename, std::ios::app), tee_(bio::tee(log_file_), 1) 
    { } 

    std::streamsize read(char* s, std::streamsize n) 
    { 
     return tee_.read(in_,s,n); 
    } 

private: 
    std::istream &        in_; 
    bio::file         log_file_; 
    bio::inverse< bio::tee_filter<bio::file> > tee_; 
}; 

/*****************************************************************************/ 

typedef bio::filtering_istream    tee_istream_t; 
typedef std::tr1::shared_ptr<tee_istream_t> tee_istream_ptr_t; 

/*****************************************************************************/ 

inline tee_istream_ptr_t make_tee_istream(std::istream & in, const std::string & filename) 
{ 
    return tee_istream_ptr_t(new tee_istream_t(tee_source(in , filename), 0)); 
} 

/*****************************************************************************/ 

#endif 
3

En utilisant Boost.IOStreams, vous pouvez définir un filtre d'entrée qui enregistre ce qu'il lit dans saboterie. Quelque chose comme:

(avertissement: code non testé avant)

class LoggingInputFilter : public multichar_input_filter { 
public: 
    template<typename Source> 
    std::streamsize read(Source& Src, char* S, std::streamsize N) 
    { 
     streamsize result = read(Src, S, N); 
     if (result == -1){ 
      return result; 
     } 

     if (std::clog.write(S, result)){ 
      return result; 
     } 

     return -1; 
    } 
}; 

chaîne avec std :: cin:

LoggingInputFilter cin_logger; 
filtering_stream logged_cin(cin_logger); 
logged_cin.push(std::cin); 

et utiliser logged_cin au lieu de std :: cin

Modifier : Ou opérez au niveau de streabuf, de sorte que votre code utilise toujours std :: cin:

LoggingInputFilter cin_logger; 
filtering_streambuf logged_cin(cin_logger); 
logged_cin.push(std::cin.rdbuf()); 
std::cin.rdbuf(logged_cin); 
+0

Ce n'était pas le J'ai utilisé la solution - mais c'est ce qui m'a permis d'être sur la bonne voie. Merci! – mmocny

1

J'ai trouvé une solution simple:

Boost :: iostreams fournit une inversion entre les filtres source/collecteur.

Alors que tee_filter est modélisé comme un évier, vous pouvez inverser() dans une source, et il sera toujours « tee » ce qu'il filtre à l'évier indiqué:

boost::iostreams::file log_file("sample.txt", std::ios::trunc); // or std::ios::app 

    // Single parameter tee() function returns a tee_filter , and invert() inverts that filter 
    boost::iostreams::filtering_istream in(
      boost::iostreams::invert(
        boost::iostreams::tee(log_file))); 

De cette façon, je l'exploitation forestière sur toutes les entrées filtrées.

La performance n'est pas un problème, mais si quelqu'un remarque des drapeaux rouges, je serais très intéressé. Merci.

+0

Bon à savoir! J'ai pensé à invert(), mais je n'étais pas certain que cela puisse fonctionner, et je n'ai pas eu le temps de l'essayer. –

+0

Je viens de tester à nouveau ce code et il va segfault sauf si vous "terminez" le "tuyau" en ajoutant in.push (std :: cin); ou toute autre "Source". – mmocny

0

Belle façon facile/court (besoin dernier coup de pouce que je devine et std = C++ 11) - grâce à @mmocny, @ Éric Malenfant et Boost lib devs.

Ce programme de journaux de démonstration à « tmp.log » quelle que soit la conversation que vous avez sur std :: cin et std :: Cout:

#include <iostream> 

#include <boost/iostreams/tee.hpp> 
#include <boost/iostreams/filtering_stream.hpp> 
#include <boost/iostreams/device/file.hpp> 

namespace bio = boost::iostreams; 

int main(int, char**) 
{ 
    // Logging 
    bio::filtering_ostream log; 
    log.push(bio::file_sink("tmp.log", std::ios_base::app)); 

    // Tee filter instance (will be copied into each filter stream) 
    const bio::tee_filter<std::ostream> teeFilter(log); 

    // std::out tee 
    bio::filtering_ostream out; 
    out.push(teeFilter); 
    out.push(std::cout); 

    // std::in tee 
    bio::filtering_istream in; 
    in.push(teeFilter, 0); // If you don't pass 0 for buffer size, on std::cin it'll try to read 4096 chars and basically be useless 
    in.push(std::cin, 0); 

    out << "What is your name ?" << std::endl << std::flush; 
    std::string name; 
    getline(in, name); 
    out << "Hello " << name << std::endl << std::flush; 
} 
Questions connexes