2016-12-20 1 views
0

In einigen Sprachen wie Ruby gibt es eine phantastische Syntax zum Erstellen der formatierten Ausgabe als ein "Einzeiler" zum Teil, weil dies häufig in Ruby-Anwendungen benötigt wird.Welche schlimmen Dinge würden passieren, wenn `std :: ostringstream` einen `operator <<` hat, der `std :: ostringstream &` zurückgibt?

Es wird auch häufig in einigen C++ - Anwendungen benötigt. Hier einige Beispiel-Code:

if (num_doggies > num_biscuits) { 
    std::ostringstream ss; 
    ss << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!"; 
    log_warn(ss.str()); 
    this->negotiate_biscuit_reduction(); 
} 

Einige Open-Source-C++ 98 Projekte eine Abhilfe, die wie folgt aussieht:

struct string_formatter { 
    std::ostringstream ss; 

    template <typename T> 
    string_formatter & operator << (const T & t) { 
    ss << t; 
    return *this; 
    } 

    operator std::string() const { 
    return ss.str(); 
    } 
}; 

Damit sie so etwas schreiben können:

if (num_doggies > num_biscuits) { 
    log_warn(string_formatter{} << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!"); 
    this->negotiate_biscuit_reduction(); 
} 

drei Zeilen in eine Zeile umwandeln.

Allerdings sieht die string_formatter Klasse aus Code-Review-Sicht, vor allem aufgrund dieser impliziten Konvertierung ziemlich böse. Implizite Konvertierungen werden normalerweise als ziemlich böse angesehen, und sie sind böser, wenn sie in gängige oder primitive Typen konvertiert werden. wie int oder std::string.

In C++ 11 können wir es leicht ändern:

operator std::string() && { 
    return ss.str(); 
    } 

Nun ist die Umwandlung wird nur ausgelöst, wenn die string_formatter eine r-Wert Referenz. So ist es weniger wahrscheinlich zu lösen, aber es ist auch komplizierter. Ist es eigentlich weniger böse? Debattierbar.

Erwähnenswert: andere haben noch aufwändigere Hacks weg zu bekommen diese ohne string_formatter Shim zu arbeiten: https://codereview.stackexchange.com/questions/6094/a-version-of-operator-that-returns-ostringstream-instead-of-ostream

keine impliziten Konvertierungen hier, aber wir sind Betreiber auf Standard-Bibliothekstypen zu überlasten, die in der Regel eine Nein, nein.


Lassen Sie uns ein wenig zurück. Warum kann ich nicht Kompromisse machen und dies tun? Es ist zumindest ein wenig kompakter:

if (num_doggies > num_biscuits) { 
    std::ostringstream ss; 
    log_warn((ss << "There are " << num_doggies" << " puppy dogs and only " << num_biscuits << " biscuits!").str()); 
    this->negotiate_biscuit_reduction(); 
} 

Es stellt sich heraus, dass dies nicht kompilieren, weil std::ostringstream::operator << kehrt std::ostream & und nicht std::ostringstream &. Zu der Zeit, die alle << aufgelöst haben, haben wir nur eine ostream &, so dass wir .str() nicht verwenden können.

Meine Frage ist, warum ist das der Fall?

Warum nicht std::ostringstream::operator << zurückgeben std::ostringstream &?Ist es:

  • Unerwünschte aus technischen oder sicherheitstechnischen Gründen
  • unnötige Schwierigkeiten für Standardbibliothek Implementierer
  • Vermächtnis Hinzufügen - es war immer so und es ändert sich jetzt vielleicht Code brechen. (Aber welcher Code würde genau dadurch gebrochen werden? std::ostringstream & kann aufgrund einer Vererbungsbeziehung durch eine Standardkonvertierung in std::ostream & umgewandelt werden?)
  • Obscurity - niemand kümmert sich!

Hinweis: Ich kenne verschiedene Möglichkeiten, string_formatter und Logger-Klassen zu entwerfen. Ich bin speziell interessiert, ob es Design-Gründe in der Standard-Bibliothek gibt, die es viel besser für std::ostringstream machen, std::ostream & zurückzugeben.

+0

Sie können ein '.str()' auf 'string_formatter' triviale Weise hinzufügen, nicht wahr? –

+0

@ T.C. Ja, aber das ist nicht wirklich der Punkt, ich bin mehr interessiert, wenn es einen guten Grund gibt, verstehe ich nicht, warum 'std :: ostringstream' 'std :: ostream &' –

+0

zurückgeben muss, weil es keinen Operator << in ostringstream gibt ? Es verwendet grundlegende ostream durch Magie der Vererbung. –

Antwort

0

Es ist nicht klar aus Ihrem Beitrag, ob Sie interessieren würden, was die operato<< Funktionen zurückgeben, wenn Sie Ihren Code vereinfachen können, der sich mit der Protokollierung der Warnmeldungen befasst.

Hier ist eine Möglichkeit, den Code zu vereinfachen, der Warnmeldungen protokolliert.

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

void log_warn(std::string const& s) 
{ 
    std::cout << s; 
} 

struct log_warning_impl 
{ 
    mutable std::ostringstream ss; 

    ~log_warning_impl() 
    { 
    log_warn(ss.str()); 
    } 
}; 

template <typename T> 
log_warning_impl const& operator<<(log_warning_impl const& l, const T & t) 
{ 
    l.ss << t; 
    return l; 
} 

log_warning_impl const& operator<<(log_warning_impl const& l, std::ostream&(*f)(std::ostream&)) 
{ 
    l.ss << f; 
    return l; 
} 


#define WARNING_LOGGER log_warning_impl{} 

int main() 
{ 
    WARNING_LOGGER << "There is a problem in the " << 10 << "-th line of the data file" << std::endl; 
} 

In Ihrem Fall können Sie einfach:

if (num_doggies > num_biscuits) 
{ 
    WARNING_LOGGER << "There are " << num_doggies << " puppy dogs and only " << num_biscuits << " biscuits!"; 
} 
Verwandte Themen