Можно ли во время выполнения выбрать функцию шаблона? Что-то вроде:Динамическая диспетчеризация функций шаблона?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
Можно ли во время выполнения выбрать функцию шаблона? Что-то вроде:Динамическая диспетчеризация функций шаблона?
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
Типичный «трюк» для того, чтобы скомпилировать время и время выполнения при работе с шаблонами, посещает вариант типа. Вот как это делает библиотека общего изображения (доступная как 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)
.
NO
Шаблонов реализовать время компиляции полиморфизма не запускайте время полиморфизма.
Нет, шаблоны являются функцией времени компиляции, а i
не известно во время компиляции, так что это невозможно. A<I>::foo()
следует адаптировать к чему-то вроде A::foo(i)
.
Мне это известно. Я блуждаю, если, может быть, есть обходное решение. – Predrag
Обходное решение - либо сделать 'i' известным во время компиляции (например, решение @ iammilind), либо сделать' A :: foo' не требующим аргумента времени компиляции (мое решение). – tenfour
Существует третий способ (если имеется ограниченное количество вариантов): постройте таблицу поиска, создав все функции во время компиляции, а затем отправьте их одному из них во время выполнения. Я использовал это раньше, и это болезненно, но возможно. –
Аргумент шаблона должен быть известен во время компиляции. Таким образом, компилятор не может пройти A<i>::foo()
.
Если вы хотите работать вокруг, то вы должны сделать bar()
также template
:
template<int i>
void bar() {
A<i>::f(); // ok
}
Для этого, вы должны знать аргумент bar()
во время компиляции.
В зависимости от того, что вы хотите сделать точно (т. Е. Существует небольшое количество ограниченных экземпляров, которые вы хотите использовать?), Вы можете создать таблицу поиска, а затем использовать ее динамически. Для полностью ручного подхода, с вариантами 0, 1, 2 и 3, вы можете сделать:
void bar(int i) {
static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
lookup[i]();
}
Конечно, я выбрал самый простой вариант для примера. Если нужный вам номер не является последовательным или нулевым, вы можете предпочесть std::map<int, void (*)(void) >
, а не массив. Если количество различных вариантов, которые вы хотите использовать, больше, вы можете добавить код, чтобы автоматически вводить в действие шаблоны, а не вручную вводить их все ... Но вам нужно было бы подумать, что каждое создание шаблона создает новый функции, и вы можете проверить, действительно ли вам это нужно.
EDIT: Я написал post, реализуя ту же инициализацию, используя только функции C++ 03, это слишком долго для ответа.
Люк Дантон написал интересный ответ here, который включает в себя, помимо прочего, инициализацию таблицы поиска с использованием конструкций C++ 0x. Мне не совсем нравится, что это изменение интерфейса требует дополнительного аргумента, но это легко решить через промежуточного диспетчера.
Это то, что я искал. Можете ли вы показать мне, как автоматически инициализировать массив или карту (или направить меня где-нибудь)? – Predrag
@Predrag: Сколько различных значений вы хотите? Они последовательны? с нуля? Также обратите внимание, что каждый экземпляр создаст новую функцию, а это, в свою очередь, означает, что это сделает ваш двоичный файл ... –
Они основаны на нулевом значении, и во время компиляции я знаю, сколько их существует. – Predrag
Могу ли я заставить компилятор развернуть его для меня, если я знаю (во время компиляции), сколько дел есть? – Predrag
Извините за то, что вы больно ... но мое знание количества случаев происходит от оператора 'size ...()'. Боюсь, что препроцессор в этом случае не поможет. Можете ли вы подумать о чем-то, что может помочь мне с этим ограничением? – Predrag
@Predrag Выберите верхний предел, достаточно большой, и вам будет удобно. –