2011-08-17 8 views

risposta

12

Un tipico "trucco" per il bridge del tempo di compilazione e del runtime quando si ha a che fare con i modelli è la visualizzazione di un tipo di variante. Questo è ciò che la Generic Image Library (disponibile come Boost.GIL o standalone), ad esempio. Si richiede in genere la forma di:

typedef boost::variant<T, U, V> variant_type; 
variant_type variant = /* type is picked at runtime */ 
boost::apply_visitor(visitor(), variant); 

dove visitor è un funtore polimorfico che semplicemente in avanti per il modello:

struct visitor: boost::static_visitor<> { 
    template<typename T> 
    void 
    operator()(T const& t) const 
    { foo(t); } // the real work is in template<typename T> void foo(T const&); 
}; 

Questo ha il bel design che l'elenco dei tipi di che il modello sarà/può essere istanziato con (qui, il sinonimo del tipo variant_type) non è accoppiato al resto del codice. Metafunzioni come boost::make_variant_over consentono anche calcoli sull'elenco di tipi da utilizzare.

Poiché questa tecnica non è disponibile per i parametri non di tipo, è necessario "srotolare" la visita a mano, il che significa purtroppo che il codice non è leggibile/gestibile.

void 
bar(int i) { 
    switch(i) { 
     case 0: A<0>::f(); break; 
     case 1: A<1>::f(); break; 
     case 2: A<2>::f(); break; 

     default: 
      // handle 
    } 
} 

Il solito modo di affrontare la ripetizione nel commutatore sopra è a (ab) utilizzare il preprocessore. Un (non testato) esempio con Boost.Preprocessor:

#ifndef LIMIT 
#define LIMIT 20 // 'reasonable' default if nothing is supplied at build time 
#endif 
#define PASTE(rep, n, _) case n: A<n>::f(); break; 

void 
bar(int i) { 
    switch(i) { 
     BOOST_PP_REPEAT(LIMIT, PASTE, _) 

     default: 
      // handle 
    } 
} 

#undef PASTE 
#undef LIMIT 

Meglio trovare buoni, i nomi di auto-documentazione per LIMIT (non sarebbe male per PASTE o), e limitare il sopra di generazione di codice a un solo sito.


costruzione dalla soluzione e i tuoi commenti di David:

template<int... Indices> 
struct indices { 
    typedef indices<Indices..., sizeof...(Indices)> next; 
}; 

template<int N> 
struct build_indices { 
    typedef typename build_indices<N - 1>::type::next type; 
}; 

template<> 
struct build_indices<0> { 
    typedef indices<> type; 
}; 

template<int... Indices> 
void 
bar(int i, indices<Indices...>) 
{ 
    static void (*lookup[])() = { &A<Indices>::f... }; 
    lookup[i](); 
} 

poi a chiamare bar: bar(i, typename build_indices<N>::type()) dove N sarebbe la vostra costante costante di tempo, sizeof...(something). È possibile aggiungere un livello per nascondere il 'brutto' di quella chiamata:

template<int N> 
void 
bar(int i) 
{ bar(i, typename build_indices<N>::type()); } 

che si chiama come bar<N>(i).

+0

Posso compilare il compilatore per me se so (al momento della compilazione) quanti casi ci sono? – Predrag

+0

Mi dispiace per il dolore ... ma la mia conoscenza del numero di casi deriva dall'operatore 'size ...()'. Temo che il preprocessore non sarà d'aiuto in quel caso. Puoi pensare a qualcosa che può aiutarmi con questo vincolo? – Predrag

+0

@Predrag Scegli un limite superiore abbastanza grande e comodo. –

1

NO
Modelli implementare tempo di compilazione il polimorfismo non correre il tempo polimorfismo.

4

No, i modelli sono una funzione in fase di compilazione e i non è noto in fase di compilazione, quindi questo è impossibile. A<I>::foo() dovrebbe essere adattato a qualcosa come A::foo(i).

+0

Ne sono consapevole. Mi aggiro se forse c'è qualche soluzione. – Predrag

+1

La soluzione alternativa consiste nel rendere 'i' noto in fase di compilazione (come la soluzione di @ iammilind), oppure fare in modo che' A :: foo' non richieda un argomento in fase di compilazione (la mia soluzione). – tenfour

+4

Esiste un terzo modo (se esiste un numero limitato di scelte): creare una tabella di ricerca istanziando tutte le funzioni in fase di compilazione e quindi inviare a una di esse in fase di runtime. L'ho usato prima ed è doloroso, ma fattibile. –

1

L'argomento del modello deve essere noto al momento della compilazione. Quindi non c'è modo per il compilatore di passare A<i>::foo().

Se si vuole aggirare allora dovete fare bar() anche un template:

template<int i> 
void bar() { 
    A<i>::f(); // ok 
} 

Per questo, è necessario conoscere argomento per bar() in fase di compilazione.

8

A seconda di cosa si vuole fare esattamente (cioè c'è un piccolo numero di istanze limitate che si desidera utilizzare?) È possibile creare una tabella di ricerca e quindi utilizzarla dinamicamente. Per un approccio completamente manuale, con le opzioni 0, 1, 2 e 3, si potrebbe fare:

void bar(int i) { 
    static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo }; 
    lookup[i](); 
} 

Naturalmente, ho scelto l'opzione più semplice per l'esempio. Se il numero che ti interessa non è consecutivo o a base zero, potresti preferire uno std::map<int, void (*)(void) > piuttosto che un array. Se il numero di scelte differenti che desideri utilizzare è maggiore, potresti voler aggiungere del codice per intantire automaticamente i modelli, invece di digitare manualmente tutti loro ... Ma dovresti considerare che ogni istanza del modello crea un nuovo funzione, e potresti voler verificare se effettivamente ne hai bisogno.

EDIT: Ho scritto un post implementando la stessa inizializzazione utilizzando solo le caratteristiche di C++ 03, sembrava troppo lungo per una risposta.

Luc Danton ha scritto una risposta interessante here che include tra l'altro l'inizializzazione della tabella di ricerca utilizzando i costrutti C++ 0x. Non mi piace molto da quella soluzione che cambia l'interfaccia per richiedere un argomento extra, ma che può essere facilmente risolto tramite un dispatcher intermedio.

+0

Questo è quello che stavo cercando. Puoi mostrarmi come inizializzare automaticamente array o mappa (o indirizzarmi da qualche parte)? – Predrag

+0

@Predrag: quanti valori diversi vuoi? Sono consecutivi? a base zero? Nota inoltre che ogni istanza creerà una nuova funzione, e questo a sua volta significa che farà crescere il tuo binario ... –

+0

Sono a base zero e so al momento della compilazione quanti ne esistono. – Predrag

Problemi correlati