Domanda

Ho scoperto template metaprogramming più di 5 anni fa e ha un enorme calcio di lettura C++ Moderno Design ma non ho mai trovato un opertunity a utilizzare nella vita reale.

Sono si mai usato questa tecnica nel codice reale?

Collaboratori Boost non è necessario applicare ;o)

È stato utile?

Soluzione

Una volta ho usato template metaprogramming in C++ per implementare una tecnica chiamata "simbolico perturbazione" per trattare con degenerare ingresso in algoritmi geometrici.Rappresentando espressioni aritmetiche nidificato modelli (es.fondamentalmente scrivendo il parse tree a mano) sono stato in grado di consegnare tutte le analisi di espressione per il modello di processore.

Facendo questo tipo di cosa con i modelli più efficiente rispetto, ad esempio, la scrittura di espressione alberi utilizzo di oggetti e facendo l'analisi in fase di runtime.E ' più veloce di quanto il modificato (turbato) struttura ad albero dell'espressione è quindi disponibile per l'ottimizzatore allo stesso livello come il resto del codice, in modo da ottenere il pieno dei benefici di ottimizzazione, sia all'interno di espressioni, ma anche (se possibile) tra le espressioni e le circostanti codice.

Naturalmente, si potrebbe ottenere la stessa cosa con l'implementazione di una piccola DSL (domain specific language) per le vostre espressioni e incolla tradotto il codice C++ nel vostro programma normale.Che vorresti ottenere gli stessi benefici di ottimizzazione e anche essere più leggibile -- ma il compromesso è che si deve mantenere un parser.

Altri suggerimenti

Ho trovato politiche, descritto in C++ Moderno Design, davvero utile in due casi:

  1. Quando sto sviluppando un componente che mi aspetto sarà riutilizzato, ma in un modo leggermente diverso.Alexandrescu il suggerimento di utilizzare un criterio per riflettere un design che si adatta davvero bene qui - mi aiuta a superare domande come, "ho potuto fare questo con un thread in background, ma se qualcuno in seguito ha voglia di farlo in intervalli di tempo?" Ok bene, ho solo scrivere la mia classe per accettare un ConcurrencyPolicy e implementare quello che ho bisogno in questo momento.Quindi almeno so che la persona che viene dietro di me può scrivere e inserire una nuova politica quando ne hanno bisogno, senza dover totalmente rielaborare il mio design.Caveat:Ho a regnare me a volte o questo può uscire di controllo -- ricordate il YAGNI principio!

  2. Quando sto cercando di effettuare il refactoring simili blocchi di codice in uno.Di solito il codice copia-incollato e modificato leggermente, perché sarebbe troppo if/else logica, altrimenti, perché i tipi in questione erano troppo diversi.Ho trovato che la politica spesso consentono una pulizia one-fits-all, in versione tradizionale logica o l'ereditarietà multipla non.

Io l'ho utilizzato i cicli interni di un gioco è la grafica di codice in cui si desidera che un certo livello di astrazione e la modularità, ma non può pagare il costo di rami o virtuale chiamate.Nel complesso è stata una soluzione migliore rispetto a una proliferazione di manoscritta in casi particolari funzioni.

Template metaprogramming e modelli di espressione stanno diventando sempre più popolare nella comunità scientifica come metodi di ottimizzazione che offload alcune sforzo computazionale sul compilatore pur mantenendo alcune astrazione.Il codice risultante è più grande e meno leggibile, ma ho usato queste tecniche per velocizzare algebra lineare librerie e quadratura metodi FEM librerie.

Per l'applicazione specifico di lettura, Todd Veldhuizen è un grande nome in questo settore.Un libro popolare è C++ e agli Oggetti di Calcolo Numerico per Scienziati e Ingegneri da Daoqi Yang.

Modello meta di programmazione è un meraviglioso e la potenza tecnica durante la scrittura di c++ librerie.Io l'ho usato qualche volta con soluzioni personalizzate, ma di solito meno di un elegante palazzo in stile c++ soluzione è più facile per ottenere attraverso la revisione del codice e più facile da mantenere per altri utenti.

Tuttavia, ho avuto un sacco di chilometraggio fuori di modello di meta-programmazione durante la scrittura di componenti riutilizzabili e/o librerie.Non sto parlando di nulla di grande alcuni di Spinta roba solo piccole componenti che verranno utilizzate di frequente.

Ho usato TMP per un singleton sistema in cui l'utente può specificare che tipo di singleton desiderato.L'interfaccia è molto semplice.Al di sotto di esso è stato alimentato da pioggia e TMP.

template< typename T >
T& singleton();

template< typename T >
T& zombie_singleton();

template< typename T >
T& phoenix_singleton();

Un altro uso di successo è stato, semplificando il nostro IPC livello.È costruito utilizzando il classico OO stile.Ogni messaggio deve derivare da una classe base astratta e l'override di alcuni metodi di serializzazione.Niente di troppo estremo, ma genera un sacco di caldaia piastra di codice.

Abbiamo buttato alcune TMP e la generazione automatizzata di tutto il codice per il semplice caso di messaggi contenenti solo CONTENITORE di dati.Il TMP messaggi ancora le OO backend ma ridurre notevolmente la quantità di caldaia piastra di codice.Il TMP è stato utilizzato anche per generare il messaggio di visitatore.Nel tempo tutto il nostro messaggio di migrazione per il TMP metodo.Era più facile e meno codice per creare un semplice CONTENITORE struct solo per passare il messaggio e aggiungere il paio (forse 3) linee necessarie per ottenere il TMP per generare le classi che derivano un nuovo messaggio da inviare una classe regolare attraverso l'IPC quadro.

Io uso il template metaprogramming tutto il tempo, ma in D, non C++.C++'s modello metalinguaggio è stato originariamente progettato per il semplice tipo di parametrizzazione e divenne un Turing completo metalinguaggio quasi per caso.Si tratta pertanto di un Turing tarpit che solo Andrei Alexandrescu, non mortali, possibile utilizzare.

D modello di linguaggio speciale, invece, è stato effettivamente progettato per metaprogrammazione al di là della semplice tipo di parametrizzazione.Andrei Alexandrescu sembra amarla, ma altre persone possono davvero comprendere il suo D modelli.È anche abbastanza potente che qualcuno ha scritto un in fase di compilazione raytracer come prova di concetto.

Credo che la più utile/non banale metaprogram che ho scritto in D è stato un modello di funzione che, data una struttura tipo del parametro di modello e un elenco di nomi di intestazione di colonna in un ordine corrispondente alle dichiarazioni di variabile nella struttura come parametro di runtime, di leggere in un file CSV, e restituisce un array di struct, uno per ogni riga, di ogni struttura di campo corrispondente a una colonna.Tutte le conversioni di tipo (stringa di float, int, etc.) vengono eseguite automaticamente, in base alle tipologie dei campi del modello.

Un altro buono, che per lo più opere, ma ancora non gestisce alcuni casi in modo corretto, è una copia profonda template di funzione che gestisce le strutture, le classi e le matrici correttamente.Si utilizza solo la fase di compilazione, riflessione e introspezione, in modo che si può lavorare con le strutture, che, a differenza di conclamata classi, non hanno runtime di riflessione, di introspezione, di capacità in D, perché si suppone di essere leggero.

La maggior parte dei programmatori che utilizzano il template metaprogramming utilizzarlo in modo indiretto, attraverso le librerie come boost.Non anche, probabilmente, sapere cosa sta succedendo dietro le quinte, solo che rende la sintassi di alcune operazioni molto più facile.

Ho usato un bel po ' con il codice DSP, soprattutto FFTs, dimensione fissa circolare buffer di hadamard trasforma e simili.

Per chi ha familiarità con Oracle Template Library (OTL), boost::qualsiasi Loki biblioteca (quella descritta in C++ Moderno Design) ecco la prova di concetto TMP codice che consente di memorizzare una riga di otl_stream in vector<boost::any> container e di accedere ai dati di un numero di colonna.E 'Sì', io vado a inserire nel codice di produzione.

#include <iostream>
#include <vector>
#include <string>
#include <Loki/Typelist.h>
#include <Loki/TypeTraits.h>
#include <Loki/TypeManip.h>
#include <boost/any.hpp>
#define OTL_ORA10G_R2
#define OTL_ORA_UTF8
#include <otlv4.h>

using namespace Loki;

/* Auxiliary structs */
template <int T1, int T2>
struct IsIntTemplateEqualsTo{
    static const int value = ( T1 == T2 );
};

template <int T1>
struct ZeroIntTemplateWorkaround{
    static const int value = ( 0 == T1? 1 : T1 );
};


/* Wrapper class for data row */
template <class TList>
class T_DataRow;


template <>
class T_DataRow<NullType>{
protected:
    std::vector<boost::any> _data;
public:
    void Populate( otl_stream& ){};
};


/* Note the inheritance trick that enables to traverse Typelist */
template <class T, class U>
class T_DataRow< Typelist<T, U> >:public T_DataRow<U>{
public:
    void Populate( otl_stream& aInputStream ){
        T value;
        aInputStream >> value;
        boost::any anyValue = value;
        _data.push_back( anyValue );

        T_DataRow<U>::Populate( aInputStream );
    }

    template <int TIdx>
    /* return type */
    Select<
        IsIntTemplateEqualsTo<TIdx, 0>::value,
        typename T,
        typename TL::TypeAt<
            U,
            ZeroIntTemplateWorkaround<TIdx>::value - 1
        >::Result
    >::Result
    /* sig */
    GetValue(){
    /* body */
        return boost::any_cast<
            Select<
                IsIntTemplateEqualsTo<TIdx, 0>::value,
                typename T,
                typename TL::TypeAt<
                    U,
                    ZeroIntTemplateWorkaround<TIdx>::value - 1
                >::Result
            >::Result
        >( _data[ TIdx ] );
    }
};


int main(int argc, char* argv[])
{
    db.rlogon( "AMONRAWMS/WMS@amohpadb.world" ); // connect to Oracle
    std::cout<<"Connected to oracle DB"<<std::endl;
    otl_stream o( 1, "select * from blockstatuslist", db );

    T_DataRow< TYPELIST_3( int, int, std::string )> c;
    c.Populate( o );
    typedef enum{ rcnum, id, name } e_fields; 
    /* After declaring enum you can actually acess columns by name */
    std::cout << c.GetValue<rcnum>() << std::endl;
    std::cout << c.GetValue<id>() << std::endl;
    std::cout << c.GetValue<name>() << std::endl;
    return 0;
};

Per chi non ha familiarità con il citato librerie.

Il problema con OTL è otl_stream contenitore è che si può accedere alle colonne di dati solo in ordine sequenziale da dichiarazione di variabili di tipo appropriato e di applicare il operator >> per otl_stream oggetto nel seguente modo:

otl_stream o( 1, "select * from blockstatuslist", db );
int rcnum; 
int id;
std::string name;
o >> rcnum >> id >> name; 

Non è sempre conveniente.La soluzione è scrivere un po ' di classe wrapper e popolarlo con i dati otl_stream.Il desiderio è quello di essere in grado di dichiarare l'elenco dei tipi di colonna e quindi:

  • prendere il tipo T della colonna
  • dichiarare la variabile di quel tipo
  • applicare olt_stream::operator >>(T&)
  • memorizzare il risultato e il vettore di spinta: qualsiasi)
  • prendere il tipo di colonna successiva e ripetere fino a quando tutte le colonne vengono trattati

Si può fare tutto questo con l'aiuto di Loki Typelist struttura del modello di specializzazione e di eredità.

Con l'aiuto di Loki biblioteca costrutti si può anche generare un mazzo di GetValue funzioni che restituiscono valori di tipo appropriato dedurre da colonna numero (in realtà il numero di digitare Typelist).

No non ho usato nel codice di produzione.

Perché?

  1. Dobbiamo sostenere 6+ piattaforme con nativo piattaforma compilatori.È abbastanza difficile da utilizzare STL in questo ambiente figuriamoci modello moderno le tecniche.
  2. Gli sviluppatori non sembrano tenere il passo C++ progressi più.Usiamo il C++ quando abbiamo a che.Abbiamo il codice legacy legacy disegni.Il nuovo codice è fatto in qualcos'altro, ad esempio, Java, Javascript, Flash.

Quasi 8 mesi, dopo aver chiesto questo ho finalmente usato alcuni TMP, io uso un TypeList di interfacce per attuare QueryInterface in una classe di base.

Io lo uso con boost::statechart per grandi statemachines.

Io sì, per lo più a fare alcune cose che assomigliano a duck typing quando avevo il confezionamento di un legacy API in una più moderna interfaccia C++.

Non che.Il motivo dietro che è come segue:dalla natura del template metaprogramming, se qualche parte della vostra logica è fatto a tempo di compilazione, ogni logica che è dipendente deve essere effettuato al momento della compilazione pure.Una volta che si avvia, fare una parte della vostra logica in fase di compilazione, non c'è ritorno.La palla di neve che continuerà a rotolamento e non c'è modo di fermarlo.

Per esempio, non è possibile iterare sugli elementi di un boost::tupla<>, perché si può accedere solo in fase di compilazione.È necessario utilizzare template metaprogramming per realizzare quello che sarebbe stato facile e semplice da C++, e questo sempre accade quando gli utenti di C++ non sono abbastanza attento a non spostare troppe cose per la compilazione.A volte è difficile vedere quando un certo uso della fase di compilazione, logica vorrebbe diventare problematico, e a volte i programmatori sono desiderosi di provare e testare quello che ho letto in Alexandrescu s.In ogni caso, questa è una pessima idea, a mio parere.

Molti programmatori non utilizzare modelli molto a causa della scarsa compilatore supporta fino a poco tempo fa.Tuttavia, mentre i modelli hanno avuto un sacco di problemi in pas, i nuovi compilatori supportano molto meglio.Scrivo il codice che deve lavorare con GCC su Mac e Linux e Microsoft Visual C++, e non solo con GCC 4 e VC++ 2005 che questi compilatore hanno supportato lo standard davvero bene.

La programmazione generica, tramite dei modelli non è qualcosa che hai bisogno di tutto il tempo, ma è sicuramente un codice utile per avere nella vostra cassetta degli attrezzi.

L'esempio più ovvio di classi contenitore, ma i modelli sono utili anche per molte altre cose.Due esempi dal mio lavoro sono:

  • Smart pointers ( ad es.Riferimento contati, copy-on-write, etc.)
  • Matematica classi di supporto, quali Matrici, vettori, spline, etc.che devono supportare una varietà di tipi di dati e di essere ancora efficiente.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top