Динамическая диспетчеризация шаблонных функций? - PullRequest
10 голосов
/ 17 августа 2011

Можно ли во время выполнения решить, какую функцию шаблона вызывать? Что-то вроде:

template<int I>
struct A {
    static void foo() {/*...*/}
};

void bar(int i) {
    A<i>::f();   // <-- ???
}

Ответы [ 5 ]

13 голосов
/ 17 августа 2011

Типичный «трюк» для согласования времени компиляции и времени выполнения при работе с шаблонами - посещение варианта типа. Вот что делает, например, Универсальная библиотека изображений (доступная как 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).

8 голосов
/ 17 августа 2011

В зависимости от того, что именно вы хотите сделать (т.е. существует ли небольшое количество ограниченных экземпляров, которые вы хотите использовать?), Вы можете создать таблицу поиска и затем использовать ее динамически.Для полностью ручного подхода с опциями 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) >, а не массив.Если число различных вариантов, которые вы хотите использовать, больше, возможно, вы захотите добавить код для автоматической создания шаблонов, а не вводить их все вручную ... Но вы должны учитывать, что каждое создание шаблона создает новыйфункции, и вы можете проверить, действительно ли вам это нужно.

РЕДАКТИРОВАТЬ : я написал публикацию , реализующую ту же инициализацию с использованием только функций C ++ 03Это казалось слишком длинным для ответа.

Люк Дантон написал интересный ответ здесь , который включает, среди прочего, инициализацию таблицы поиска с использованием конструкций C ++ 0x.Мне не очень нравится из этого решения, что оно изменяет интерфейс, требуя дополнительного аргумента, но это легко можно решить с помощью промежуточного диспетчера.

4 голосов
/ 17 августа 2011

Нет, шаблоны являются функцией времени компиляции, а i неизвестно во время компиляции, поэтому это невозможно. A<I>::foo() должен быть адаптирован к чему-то вроде A::foo(i).

1 голос
/ 17 августа 2011

Аргумент шаблона должен быть известен во время компиляции.Таким образом, компилятор не может передать A<i>::foo().

. Если вы хотите обойти это, вы должны сделать bar() также template:

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

.вы должны знать аргумент bar() во время компиляции.

1 голос
/ 17 августа 2011

NO
В шаблонах реализован полиморфизм времени компиляции, а не полиморфизм времени выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...