ostrstream interprète la chaîne constante comme un pointeur

J’ai rencontré ce problème lors du nettoyage des macros de débogage d’une ancienne application C / C ++: nous avons une classe Tracer héritant d’ ostrstream (je sais qu’elle est obsolète depuis le C ++ 98, mais cette application a été écrite en 1998!). utiliser comme ceci:

 Tracer() << "some" << " message" << " here"; 

Maintenant, si la première valeur de la chaîne est une chaîne constante comme ci-dessus, le résultat de l’appel de ostrstream::str() sur le Tracer (effectué dans le destructeur, l’insertion du résultat dans une queue) contient une représentation hexadécimale du pointeur. à cette chaîne au lieu du texte. Ainsi, la déclaration ci-dessus produirait quelque chose comme "0x401a37 message here" . Cela ne s’est pas produit avec les anciennes macros car elles avaient toujours un long (ID de thread) en tant que première valeur qui a maintenant été supprimée.

Entrer dedans avec gdb a montré que lors de la première insertion, cela appelait l’ operator<<(void const*) sur le stream ostr, tandis que les insertions suivantes appelaient l’ operator<< (basic_ostream&, char const*) (modèle supprimé pour plus de lisibilité).

Quelqu’un peut-il expliquer ce comportement? Quel serait un moyen propre à résoudre ce problème? J’ai trouvé une solution de contournement facile, qui utilise << left comme premier argument – est-ce sûr? Y a-t-il de meilleures façons de le faire?

Voici un exemple réduit:

 #include  #include  using namespace std; class Trace : public ostrstream { public: Trace(); virtual ~Trace(); }; Trace::Trace() : ostrstream() {} Trace::~Trace() { static_cast(*this) <<ends; char * text = ostrstream::str(); cout << "MESSAGE: "<< text <<endl; delete[] text; } int main(){ Trace() << "some" << " text" << " here"; Trace() << left << "some" << " text" << " here"; Trace() << 123 << " text" << " here"; } 

Cela fonctionne de cette façon parce que Tracer() est une valeur temporaire (rvalue) qui ne peut pas être liée à la référence non constante dans l’ operator<<(basic_ostream<...>&, ,.

Cependant, vous pouvez appeler des fonctions membres comme l’ operator<<(void const*) , car cela ne nécessite pas de valeur lvalue.

La fonction membre renvoie ensuite une référence à l'object stream qui peut être utilisée pour appeler l' operator<< suivant operator<< dans la séquence.

L'appel de n'importe quel membre fonctionne de cette façon, comme Tracer() << left ou Tracer() << flush , et «converti» ainsi la référence en une référence lvalue est relativement sûre.

Si vous possédez un compilateur conforme à C ++ 11, la bibliothèque standard contient même un operator<<(basic_ostream<...>&&, qui le fait pour vous. Dans ce cas, vous n'avez plus besoin de la solution de contournement.

Notons tout d’abord que l’ operator<< qui prend const char* comme argument est une fonction non membre. Et il existe une fonction membre qui prend void const* comme argument.

Dans votre code, l'expression Trace() << "xyz" peut uniquement appeler des fonctions membres, car Trace() crée un délai, qui ne peut pas être lié au premier paramètre des fonctions de l' operator<< non membre operator<< , car ces fonctions prennent le premier argument comme std::ostream& qui est une référence non-const. Donc, Trace() << "xyz" résolu en operator<< membre operator<< qui prend void* en argument, qui affiche l'adresse!


Mes conseils:

  • std::ostrstream pas de la classe stream ( std::ostrstream est de toute façon déconseillé ).
  • Écrivez plutôt un simple encapsuleur sur la classe de stream et l' operator<< surcharge operator<<

Voici un exemple:

 #include  //for std::ossortingngstream struct Trace { std::ossortingngstream ss; template Trace& operator << (T const & data) { ss << data; return *this; } ~Trace() { std::cout << ss.str() << std::endl; } }; 

Maintenant, vous pouvez l'utiliser comme:

 Trace() << "Hello World\n" << 100 << "\nBye\n"; 

Sortie:

 Hello World 100 Bye 

Démo en direct