2009-02-10 1 views
63

Ho usato più costrutti C++ "moderni" per un po ', ma in modo superficiale e non ovunque. Sto cercando progetti open source da studiare che siano buoni esempi di utilizzo moderno di C++ e STL.Esempi di "moderno C++" in azione?

Cose come quanto suggerito in "STL efficace" della Meyer, come ad esempio cercando di evitare for loop e sostituirli con costrutti più funzionali, utilizzando boost :: bind e boost :: funzione, ecc Questi si sentono ancora un po 'innaturale per me, e quando devo fare qualcosa velocemente e lavorando, tendo a tornare a libc e string.h (puoi avere il mio strtok quando lo fai fuori dalle mie mani fredde, morte).

Tuttavia, ho anche avuto l'esperienza positiva di trovare quello che sarebbe stato un drastico cambiamento semplificato perché ho usato questi costrutti, o di essere in grado di implementare qualcosa con solo poche righe di codice, perché avevo il diritto operatori e funtori in giro. Inoltre, di recente ho prestato più attenzione alla concorrenza, e quindi questo sta diventando ancora più importante per me.

Potete consigliare alcuni esempi di progetti open source ben scritti che fanno un uso pesante dell'STL e di altre tecniche C++ moderne che potrei studiare? Sono particolarmente interessato al codice dell'applicazione, la navigazione nelle fonti Boost è stata utile, ma è per necessità molto generale perché è il codice della libreria.

Sono interessato a progetti di dimensioni medio-grandi, almeno alcune decine di migliaia di linee. È piuttosto facile trovare esempi di qualche centinaio di righe, ma non è di grande aiuto.

+1

Posso togli il tuo strtok() ma ti restituisce strtok_r()? È thread-safe e rientranti. –

+0

Ho rinunciato a strtok quando ho scritto una classe tokenizer che restituiva un const_iterator dal suo metodo begin() per farmi scorrere i token. –

+3

Boost's String Algo, Regex, Spirit, Xpressive (e anche Tokenizer se sei sfortunato) mangeranno 'strok()' alive. :) – jfs

risposta

11

Qui potete trovare alcuni esempi interessanti di come Boost viene utilizzato in progetti open source:
http://www.boost.org/users/uses_open.html

+1

È grandioso! Mi mancano sempre i posti più ovvi da guardare .. – joeld

+1

Preferirei aver trovato un esempio eccellente, ognuno dei quali usa boost e Modern C++ a vari gradi e minori, ma questa era la migliore fonte di esempi reali (non solo frammenti) che potrei trovare. – joeld

+1

Le risposte che contengono solo collegamenti sono [considerate pratiche sbagliate] (http://meta.stackexchange.com/questions/8231/are-answers-that-just-contain-links-elsewhere-really-good-answers). Si prega di riassumere il contenuto qui (non copiare/incollare) in modo che la risposta possa reggersi da sola. Se non lo fai, corri il rischio che la tua risposta venga rimossa, soprattutto se il collegamento muore mai. –

4

io non sono sicuro di "ben scritto", ma ci sono un paio di cose là fuori come Hypertable e KFS che sono software a livello di sistema che entrambi utilizzano Boost e STL ampiamente.

Ho sentito alcune cose su OpenOffice e Google Chrome ma non ho guardato il loro codice sorgente per sapere con quale precisione usano l'STL. Ho dato uno sguardo a KDE ma non chiamerei necessariamente quel C++ moderno. Posso dirvi che un codice su cui sto lavorando sta facendo un sacco di C++ moderno ma purtroppo non è open source - anche se penso che sia solo questione di tempo e pazienza per ottenere il moderno approccio al C++ e avere più progetti open source adottando gli idiomi.

+0

Grazie, esaminerò quelli. E 'stato anche il mio sentimento che il C++ "moderno" viene usato molto nei progetti interni, ma non ha un uso molto più "pubblico" visibile. – joeld

+0

Mi piace KDE, è un C++ molto moderno, penso, usando molto fortemente l'idioma pimpl. –

15

In realtà, ho dato un'occhiata a Google Chrome e lo consiglio. La codifica C++ di Google guidelines è un buon supporto per progetti di grandi dimensioni. Vengono utilizzati/adottati anche nel nostro team. Inoltre, sono molto felice che stiano fornendo i loro framework C++ mocking e testing come progetti open source. Molto utile per progetti di grandi dimensioni in cui ti perdi un sacco di roba di prova dal mondo Java/gestito.

+7

L'ho guardato attraverso, ottimo codice, ottimo esempio ma non molto stile "Modern C++". – joeld

+6

Le linee guida per la codifica di Google sono antiche e non rappresentano un buon esempio del moderno stile C++. –

+0

Correggere il collegamento a "linee guida", per favore. – nbro

23

Meyers è OK, tuttavia, se si vuole davvero spingere te stesso, si deve leggere:

Andrei Alexandrescu - Modern C++ Design: Generic Programming and Design Patterns Applied

Si lascerà a bocca aperta. Quello che apprendi nel libro descrive lo Loki library.

Uno dei miei preferiti è il conversioni INT-a-tipo:

template <int v> 
struct Int2Type 
{ 
    enum { value = v }; 
}; 

ho usato in passato per la mia libreria serializzazione XML C++ per la pre-assegnazione vettore <> s 'prima di caricarli con dati:

// We want to call reserve on STL containers that provide that function, 
// namely std::vector. 
// However, we would get a compiler error if we tried to call reserve on 
// an STL container that 
// did not provide this function. This is the solution. 
template <bool b, class T> 
class CReserve 
{ 
public: 
    static void reserve(T &lst, const int &n) 
    { reserve(lst, n, Loki::Int2Type<b>()); } 

private: 
    static void reserve(T &lst, const int &n, Loki::Int2Type<true>) 
    { lst.reserve(n); } 

    static void reserve(T &lst, const int &n, Loki::Int2Type<false>) 
    { (void)lst; (void)n; } 
}; 

Notare le specializzazioni private di cui sopra. Bene, se si guarda da vicino, si chiama reserve(), e l'altro no. Questa è una specializzazione del modello usando un bool come tipo.

che a sua volta viene utilizzato da:

template <bool bCallReserve, class T> 
bool STLSerializeClassType(MSXML::IXMLDOMNodePtr pCurNode, T &lst, 
          CXmlArchive &archive, const char *name) 
{ 
    if(archive.IsStoring()) 
    { 
     ... 
    } else { 
     lst.clear(); 

     T::size_type nCount(0); 
     XML_ELEMENT(nCount); 

     CReserve<bCallReserve, T>::reserve(lst, nCount); 

     while(nCount--) 
     { 
      T::value_type temp; 
      temp.SerializeXml(archive, pCurNode); 
      lst.push_back(temp); 
     } 
    } 
} 

Per rendere le cose semplici in degli utenti codice C++, ho aggiunto un sacco di definizioni helper:

#define SERIALIZE_XML_STL_CLASS(list_name, bCallReserve) \ 
(HS::STLSerializeClassType<(bCallReserve)> 
    (pCurNode, (list_name), archive, (#list_name)) 
) 

Quindi nel tuo codice che aveva usare qualcosa come:

std::list<CFred> fredList; 
SERIALIZE_XML_STL_CLASS(fredList, false); 

O per vettori:

vector<CFred> fredList; 
SERIALIZE_XML_STL_CLASS(fredList, true); 

In ogni caso, smetterò di virare su ... Questo è solo mettere il semplice modello Int2Type <> a buon uso. Ci sono un sacco di cose intelligenti come ottenere il compilatore per calcolare una tonnellata di cose in anticipo con un uso intelligente delle enumerazioni. È un libro davvero fantastico.

+1

Perché non scrivere una funzione di riserva modello che non fa nulla, e quindi una specializzazione per std :: vector che chiama riserva su di esso? In questo modo il chiamante non deve passare un bool per controllare se è necessario chiamare la riserva. Lo calcola solo in base al tipo di contenitore. –

+0

Sì, adoro quel libro. Questo è esattamente il tipo di cose che sto cercando di applicare su una scala più ampia. – joeld

+3

Ho avuto un eccesso di mente. Questo codice smette di sembrare "solo scrittura" dopo aver letto il libro sopra menzionato? – Muxecoid

5

Non proprio progetti, ma qui ci sono un paio di frammenti:

utilizzo Esempio di boost :: filo/boost :: bind:

class X { void expensive_operation(int argument); }; 

int main() 
{ 
    X x; 
    boost::thread thr(boost::bind(&X::expensive_operation, &x, 1000)); 
    std::cout << "Thread is processing..." << std::endl; 
    thr.join(); 
} 

std :: copia, std :: trasformano, BOOST_FOREACH:

int main() 
{ 
    std::vector<std::string> v; 
    std::copy(std::istream_iterator<std::string>(std::cin), 
       std::istream_iterator<std::string>(), std::back_inserter(v)); 
    BOOST_FOREACH(std::string & s, v) 
    { 
     transform(s.begin(), s.end(), s.begin(), toupper); 
    } 
    std::copy(v.begin(), v.end(), 
       std::ostream_iterator<std::string>(std::cout, " ")); 
} 

Lo snippet legge dall'input dell'utente un insieme di stringhe in un vettore. Quindi, per ciascuna stringa nel vettore, convertirà in maiuscolo e infine produrrà il risultato.

Le nostre applicazioni fanno pesante uso di boost :: segnali e boost :: funzione di separare le classi dove non è momento critico, soprattutto nelle classi di interfaccia utente:

class ContactDetailPanel; 
class ContactListPanel { 
public: 
    void update(); 
    void edit_completed(bool change); 
    boost::function< void (Contact &) >& edit_contact();  
}; 
int main() 
{ 
    ContactListPanel clp; 
    ContactDetailPanel cdp; 

    clp.edit_contact() = boost::bind(&ContactDetailPanel::edit, &cdp, _1); 
    cdp.edit_completed() = boost::bind(&ContactListPanel::edit_completed, &clp, _1); 
    ui::signal_update().connect(boost::bind(&ContactListPanel::update, &clp));  
} 

Ogni pannello non ha informazioni su altri pannelli. Il codice che unisce i pannelli ha la conoscenza del flusso (per modificare un contatto utilizzare il pannello dei dettagli del contatto, notificare il completamento della modifica al pannello dell'elenco dei contatti). Inoltre, c'è un segnale globale per notificare i pannelli di aggiornamenti al modello sottostante.

Questo è particolarmente utile quando è necessario scrivere il codice di prova. Non è necessario implementare classi di simulazione per sostituire il codice funzionante per il test. I test semplicemente istanziano la classe e collegano le funzioni/i segnali al codice di test senza che la classe testata se ne accorga, seguendo il principio della minima intrusione durante i test.

+0

'boost :: to_upper (s)' potrebbe sostituire 'transform (s.begin(), s.end(), s.begin(), toupper)'. – jfs

+0

'for_each (v.begin(), v.end(), cout << _1 <<" ");' or 'foreach (stringa s, v) cout << s <<" ";' potrebbe sostituire 'std :: copy (v.begin(), v.end(), std :: ostream_iterator (std :: cout, "")); ' – jfs

+0

cout << _1 <<" "- richiede boost :: lambda, che Ho provato ma non mi sento abbastanza a mio agio per scrivere uno snippet in cima alla mia testa. Non sapevo di boost :: to_upper (s), grazie. –

3

Adobe ha pubblicato una buona dose di moderna C++ open-source il codice negli ultimi due anni, che è probabilmente la pena di verificare:

http://opensource.adobe.com/wiki/display/site/Home

credo che la loro biblioteca GIL o ha avuto, o è in fase di aggiunta a Boost. il loro STLab contiene anche un sacco di funzionalità che, da quello che ho visto, è molto pulito e molto stile STL.

+0

Ho provato a usare GIL circa un anno fa e l'ho trovato davvero sorprendente dal punto di vista del design, ma non realistico per il lavoro nel mondo reale. I tempi di compilazione erano pessimi, era incredibilmente difficile eseguire il debug e avere un tipo any_image <. Era scomodo. Lo tengo d'occhio per il futuro. – joeld

2

Ecco un frammento per concatenare vettore di stringhe in una stringa:

vector<string> vecstr; 
vecstr.push_back("abc"); 
vecstr.push_back("efg"); // etc. 
string concat = accumulate(vecstr.begin(), vecstr.end(), string(""));