Question

Je crée un enregistreur avec les sections suivantes:

// #define LOG(x) // for release mode
#define LOG(x) log(x)

log(const string& str);
log(const ostream& str);

Avec l'idée de faire:

LOG("Test");
LOG(string("Testing") + " 123");
stringstream s;
LOG(s << "Testing" << 1 << "two" << 3);

Tout cela fonctionne comme prévu, mais quand je fais:

LOG(stringstream() << "Testing" << 1 << "two" << 3);

Il ne fonctionne pas:

void log(const ostream& os)
{
  std::streambuf* buf = os.rdbuf();
  if( buf && typeid(*buf) == typeid(std::stringbuf) )
  {
    const std::string& format = dynamic_cast<std::stringbuf&>(*buf).str();
    cout << format << endl;
  }
}

résultats dans « format » contenant des données indésirables au lieu de la chaîne habituelle correcte.

Je pense que cela est parce que le ostream temporaire retourné par l'opérateur << le stringstream il survive vient.

Ou je me trompe?

(Pourquoi je suppose oui. Il fonctionne string () de cette façon? Est-ce parce qu'il renvoie une référence à lui-même?)

Je voudrais vraiment le faire de cette façon que j'éliminerons une allocation supplémentaire lors de la connexion en mode de libération.

Les pointeurs ou astuces pour le faire de cette façon seraient les bienvenus. Dans ma solution réelle, j'ai de nombreuses fonctions de journaux différents et ils sont tous plus complexes que cela. Je préférerais donc d'avoir cette mise en œuvre en quelque sorte dans le code d'appel. (Et non pas en modifiant mon #define si possible)

Juste pour vous donner une idée, un exemple d'un de mes #defines réels:

#define LOG_DEBUG_MSG(format, ...) \
  LogMessage(DEBUG_TYPE, const char* filepos, sizeof( __QUOTE__( @__VA_ARGS__ )), \
  format, __VA_ARGS__)

qui correspond VarArgs printf comme fonctions de log prenant char *, chaîne () et ostream () ainsi que des fonctions non-vararg chaîne prenant (), exception () et HRESULT.

Était-ce utile?

La solution

pense Je vois ce qui se passe. Cela produit le résultat attendu:

log(std::stringstream() << 1 << "hello");

alors que cela ne:

log(std::stringstream() << "hello" << 1);

(il écrit un numéro hexadécimal, suivi du chiffre "1")

Quelques éléments pour l'explication:

  • Un rvalue ne peut pas être lié à une référence non-const
  • Il est OK pour appeler des fonctions membres sur une base temporaire
  • std :: ostream a un opérateur membre << (void *)
  • std :: ostream a un opérateur membre << (int)
  • Pour char * l'opérateur n'est pas membre, il est opérateur << (std :: ostream &, const char *)

Dans le code ci-dessus, std :: stringstream () crée un temporaire (un rvalue). Sa durée de vie est pas problématique, car elle doit durer toute l'expression complète elle est déclarée dans (i.e., jusqu'au retour de l'appel pour vous connecter ()).

Dans le premier exemple, tout fonctionne bien parce que l'opérateur membre << (int) est d'abord appelé, puis la référence retournée peut être transmis à l'opérateur << (ostream &, const char *)

Dans le second exemple, l'opérateur << (ne peut pas être appelé avec « std :: stringstream () » comme 1er argument, comme cela nécessiterait d'être lié à une référence non const. Toutefois, l'opérateur membre < <(void *) est correct, car il fait partie.

Par ailleurs: Pourquoi ne pas définir la fonction log () comme:

void log(const std::ostream& os)
{
    std::cout << os.rdbuf() << std::endl;
}

Autres conseils

Modifier votre macro LOG() à ceci:

#define LOG(x) do { std::stringstream s; s << x; log(s.str()); } while(0)

Cela vous permettra d'utiliser la syntaxe suivante dans vos journaux de débogage, de sorte que vous ne devez pas construire manuellement le flux de chaîne.

LOG("Testing" << 1 << "two" << 3);

Définissez ensuite à rien pour la libération, et vous aurez pas d'allocations supplémentaires.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top