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)
.
Posso compilare il compilatore per me se so (al momento della compilazione) quanti casi ci sono? – Predrag
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
@Predrag Scegli un limite superiore abbastanza grande e comodo. –