2015-10-21 11 views
7

Ho una classe semantica di valore che mi piacerebbe essere "visualizzabile" nello stesso senso della classe Haskells Show o Python fornisce una funzione universale __str__() .Best practice per rendere la classe C++ "Showable" (stringa, ostream)

in C++:

  • ho potuto sovraccaricare operator<<(ostream&, ...) quindi in grado di produrre la mia classe di esempio cout
  • ho potuto sovraccaricare operator std::string() così la mia classe converte in std :: string
  • ho potuto sovraccaricare operator const char*() così la mia classe converte in const char *.
  • ho potuto scrivere un membro str(), o una funzione to_string(...) libera

Ciascuna di queste funzioni potrebbero essere definite in termini di altro. È un'opzione migliore rispetto alle altre? Sono tutte queste opzioni? Quale sarebbe il modo più elegante di fare questo dato C++ 11/14/17?

risposta

8

La domanda verrà messa in attesa in pochissimi minuti, ma condividerò ancora i miei pensieri qui.

Innanzitutto, è possibile rimuovere gli overload dell'operatore const char */std :: string() dall'elenco. Se la classe non è una stringa, non dovrebbe essere convertibile in stringa e l'abilitazione della serializzazione non trasforma la tua classe in una stringa.

Per quanto riguarda str(), to_string e operator << sono piuttosto equivalenti. Tuttavia, poiché per qualsiasi classe complessa to_string() e str() è molto probabile utilizzare internamente gli ostreams, credo che operator << sia l'opzione migliore.

+0

Sì, temevo che la domanda fosse troppo aperta, ma osavo comunque. Qual è la tua ragione per 'str()' e 'to_string()' per usare l'operatore '' internamente e non viceversa? –

+0

Ah, suppongo che intenda la preferenza su es. stringstream sopra la concatenazione manuale di stringhe. Nel qual caso la scelta per l'operatore << 'ha senso. –

+0

@StijnFrishert, esattamente. È difficile serializzare una classe complessa con una semplice concatenazione di stringhe. – SergeyA

2

Non so se si tratta di buone pratiche o no, ma ...

  1. per il debug ho sempre definisco operator<< che dà un'uscita riassunto in forma di testo (questo significa che è facile per lo streaming di oggetti per accedere file)

  2. per output formattato avrei probabilmente scegliere di implementare questo in termini di funzioni liberi: detail::x_detail_writer write_details(const X&) e poi dare detail::x_detail_writer (che è un funtore) un operator<< overload

  3. per tutto tranne l'oggetto più banale che realizzo to_string in termini di operator<< se non del tutto.

per favoreggiamento con l'uscita di debug abbiamo una classe di supporto, che va qualcosa come questo:

template<class T> 
struct make_writeable { 
    friend std::ostream& operator<<(std::ostream& os, const T& t) { 
    // example prefix... 
    os << demangle(typeid(T).name()) << "{"; 
    t.write(os); 
    // example postfix: 
    os << " }"; 
    return os; 
    } 
}; 

quindi trarre qualche classe da questo e dargli una funzione membro chiamata write:

struct X : make_writeable<X> 
{ 
    void write(std::ostream& os) const { 
    // write out members here. they will appear within the prefix and postfix 
    } 
}; 
+0

C'è un bel po 'di volte in più modi per stampare un oggetto (informazioni di debug, output formattato, ...). Sembra un altro argomento contro gli operatori di conversione (perché quale formato di output vuoi, allora?). –