Comment append une mention à l’opérateur de stream

Dans notre projet, nous utilisons l’opérateur de stream c ++ (<<) de notre modèle d'objet pour imprimer un format de données facilement lisible. Voici un exemple simplifié:

std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { oStream << "[SomeMember1: " << iOwnClass._ownMember1 << "]\n"; oStream << "[SomeMember2: " << iOwnClass._ownMember2 << "]\n"; } 

En résultant dans la journalisation:

 [SomeMember1: foo] [SomeMember2: bar] 

Ce que nous voulons maintenant, c’est pouvoir indenter le résultat de cet opérateur. Certaines classes appelantes pourraient ne pas vouloir le résultat de cette manière, mais vouloir append une mise en retrait de 2 espaces avant chaque ligne. Nous pourrions append un membre à notre classe spécifiant l’indentation, mais cela ne semble pas être une solution élégante.

Bien sûr, ce n’est pas un très gros problème, mais notre exploitation forestière serait beaucoup plus agréable si cela fonctionnait.

Merci

La solution la plus simple consiste à insérer un film filtrant entre l’ ostream et le film proprement dit. Quelque chose comme:

 class IndentingOStreambuf : public std::streambuf { std::streambuf* myDest; bool myIsAtStartOfLine; std::ssortingng myIndent; std::ostream* myOwner; protected: virtual int overflow( int ch ) { if ( myIsAtStartOfLine && ch != '\n' ) { myDest->sputn( myIndent.data(), myIndent.size() ); } myIsAtStartOfLine = ch == '\n'; return myDest->sputc( ch ); } public: explicit IndentingOStreambuf( std::streambuf* dest, int indent = 4 ) : myDest( dest ) , myIsAtStartOfLine( true ) , myIndent( indent, ' ' ) , myOwner( NULL ) { } explicit IndentingOStreambuf( std::ostream& dest, int indent = 4 ) : myDest( dest.rdbuf() ) , myIsAtStartOfLine( true ) , myIndent( indent, ' ' ) , myOwner( &dest ) { myOwner->rdbuf( this ); } virtual ~IndentingOStreambuf() { if ( myOwner != NULL ) { myOwner->rdbuf( myDest ); } } }; 

Pour l’insérer, il suffit de créer une instance du streambuf:

 IndentingOStreambuf indent( std::cout ); // Indented output... 

Lorsque le indent est hors de scope, tout revient à la normale.

(Pour la journalisation, j’en ai un qui est un peu plus complexe: LoggingOStreambuf prend __FILE__ et __LINE__ comme arguments, définit myIndent sur une chaîne mise en forme avec ces arguments, plus un horodatage, le réinitialise à une chaîne d’indentation après chaque sortie, collecte tout de la sortie dans un std::ossortingngstream et le std::ossortingngstream manière atomique à myDest dans le destructeur.)

Cela peut être fait en utilisant un manipulateur de stream personnalisé qui stocke le niveau d’indentation souhaité dans un mot du tableau extensible interne du stream. Vous pouvez demander un tel mot en utilisant la fonction ios_base::xalloc . Cette fonction vous donnera l’index de votre mot. Vous pouvez y accéder en utilisant ios_base::iword . Une façon d’implémenter cela serait la suivante:

 struct indent { indent(int level) : level(level) {} private: friend std::ostream& operator<<(std::ostream& stream, const indent& val); int level; }; std::ostream& operator<<(std::ostream& stream, const indent& val) { for(int i = 0; i < val.level; i++) { stream << " "; } return stream; } std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { oStream << indent(oStream.iword(index)) << "[SomeMember1: " << iOwnClass._ownMember1 << "]\n"; oStream << indent(oStream.iword(index)) << "[SomeMember2: " << iOwnClass._ownMember2 << "]\n"; } 

Vous devez trouver où stocker l' index . Cela vous permet effectivement d'append un état personnalisé au stream (notez que cela ne serait pas prêt à l'emploi avec les threads). Chaque fonction qui veut indentation doit append l'indentation demandée au stream et la soustraire à nouveau lorsqu'elle est terminée. Vous pouvez vous assurer que cela se produit toujours en utilisant un garde pour append / soustraire le retrait souhaité (à mon humble avis, cela est plus élégant que d'utiliser un manipulateur):

 class indent_guard { public: indent_guard(int level, std::ostream& stream, int index) : level(level), stream(stream), index(index) { stream.iword(index) += level; } ~indent_guard() { stream.iword(index) -= level; } private: int level; std::ostream& stream; int index; }; 

Vous pourriez l'utiliser comme ceci:

 void some_func() { indent_guard(2, std::cout, index); // all output inside this function will be indented by 2 spaces some_func(); // recursive call - output will be indented by 4 spaces // here it will be 2 spaces again } 

La meilleure façon de faire est d’append une variable globale, qui indique l’indentation. Quelque chose comme ça :

 std::ssortingng OwnClassIndentation; std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { oStream << "[SomeMember1:" << OwnClassIndentation << iOwnClass._ownMember1 << "]\n"; oStream << "[SomeMember2:" << OwnClassIndentation << iOwnClass._ownMember2 << "]\n"; } 

Et puis définissez-le comme il convient.

Vous pouvez créer votre propre classe de stream qui comporte une variable d’indentation et remplacer le endl de cette classe en insérant l’indentation.