Типичный «трюк» для согласования времени компиляции и времени выполнения при работе с шаблонами - посещение варианта типа. Вот что делает, например, Универсальная библиотека изображений (доступная как Boost.GIL или автономная). Обычно он принимает вид:
typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);
, где visitor
- полиморфный функтор, который просто пересылает шаблон:
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&);
};
Это имеет приятный дизайн, поскольку список типов, с которыми будет / может быть создан экземпляр шаблона (здесь, синоним типа variant_type
), не связан с остальной частью кода. Метафункции, такие как boost::make_variant_over
, также позволяют использовать вычисления в списке типов.
Поскольку этот метод недоступен для нетиповых параметров, вам необходимо «развернуть» посещение вручную, что, к сожалению, означает, что код не так удобен для чтения / сопровождения.
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
}
}
Обычный способ справиться с повторениями в вышеприведенном переключателе - это (ab) использовать препроцессор. (Непроверенный) пример с использованием 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
Лучше найти хорошие, самодокументированные имена для LIMIT
(не повредит и для PASTE
), а также ограничить генерацию кода выше только одним сайтом.
Построение из решения Дэвида и ваших комментариев:
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]();
}
затем позвонить bar
: bar(i, typename build_indices<N>::type())
, где N
будет вашей постоянной времени, sizeof...(something)
. Вы можете добавить слой, чтобы скрыть «уродство» этого вызова:
template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }
который называется bar<N>(i)
.