2016-05-16 3 views
1

Это продолжение вопроса this.Диспетчер методов обработки шаблонов

У меня есть различные методы шаблонов, каждый из которых для различного рода сообщения, используя параметры шаблона не типа и специализации шаблона:

namespace Handlers { 

enum MSG_TYPES { 
    MSG1, 
    MSG2 

}; 

template<MSG_TYPES> 
void handle_message() { 
    // Default handler : not defined type 
} 


template<> 
void handle_message<MSG1>() { 
    cout << "Handle 1"; 
} 

template<> 
void handle_message<MSG2>() { 
    cout << "Handle 2"; 
} 

Теперь я хотел бы иметь какой-то другой способ, чтобы направить в правильный обработчик. Что-то вроде

template<typename T> 
void handle(T t) { 
    try { 
     handle_message<T>(); 
    } catch(...) { 

    } 
} 

, которые могут быть вызваны как

int i = 0; 
Handlers::handle(static_cast<Handlers::MSG_TYPES>(i)); 

Так, как это диспетчерская может быть достигнуто?

PS: Предыдущий код не будет работать на handle_message<T>(); из-за

Примечание: аргумент шаблона вычет/замена не удалась:

не должен по умолчанию обработчик будет вызван?

+4

Какую функцию вызывать - это решение * компиляции-времени *, но похоже, что ваше значение 'i' может быть неизвестно до * времени выполнения *. – aschepler

+0

@aschepler Да, я смешиваю время выполнения и время компиляции. Итак, я остался с подобной коммутатору структурой, чтобы решить, какой метод вызывать, как только я узнаю идентификатор сообщения (i)? – perencia

+1

Вы передаете тип шаблону, ожидающему значение перечисления; неудивительно, что он пытался вывести аргумент. –

ответ

1

Ваш метод должен быть что-то вроде:

void handle(MSG_TYPES type) { 
    switch (type) { 
     case MSG1: handle_message<MSG1>(); 
     case MSG2: handle_message<MSG2>(); 
    } 
} 
1

Если вы не хотите писать Переключатель ничего о себе, вы можете создать итерации во время компиляции, используя, например, повышение :: MPL:

#include <iostream> 

#include <boost/mpl/for_each.hpp> 
#include <boost/mpl/range_c.hpp> 

namespace Handlers { 

enum MSG_TYPES { 
    MSG1, 
    MSG2, 
    LAST 
}; 


template<MSG_TYPES> 
void handle_message() { 
} 

template<> 
void handle_message<MSG_TYPES::MSG1>() { 
    std::cout << "Handle 1" << std::endl; 
} 

template<> 
void handle_message<MSG_TYPES::MSG2>() { 
    std::cout << "Handle 2" << std::endl; 
} 


struct runtime_handler 
{ 
    std::size_t i; 
    bool& found; 

    runtime_handler(std::size_t i, bool& found) : i(i), found(found){} 

    template < typename Index > 
    void operator() (Index&) 
    { 
    if(i == Index::value) 
    { 
     Handlers::handle_message<Index::value>(); 
     found = true; 
    } 
    } 
}; 

void handle(std::size_t i) 
{ 
    bool found = false; 
    boost::mpl::for_each<boost::mpl::range_c<Handlers::MSG_TYPES, Handlers::MSG1, Handlers::LAST> >(runtime_handler(i, found)); 

    if (!found) { 
     std::cout << "could not find handler for id = " << i << std::endl; 
    } 
} 
} 


int main() 
{ 
    Handlers::handle(0); 
    Handlers::handle(1); 
    Handlers::handle(10); 
    return 0; 
} 

live example

Этого пример сравнивает значение времени с каждым из значений перечислений и выполняет соответствующие специализации, если она существует. Это может быть оптимизировано с помощью бинарного поиска, возвращаясь несколько раз в значения были найдены и т.д.

+0

Очень интересно! – perencia

1

Еще одна возможная реализация ...

unordered_map<int, function<void(void)>> handlers 
{ 
    {1, [](){cout << "Handler 1" << endl;}}, 
    {2, [](){cout << "Handler 2" << endl;}}, 
    {3, [](){cout << "Handler 3" << endl;}} 
}; 

И тогда, когда вы хотите вызвать обработчик для значения of 'i' ...

handlers[i](); 

Но самое лучшее, что нужно сделать IMO - использовать оператор switch. Кроме того, если вы собираетесь использовать перечисления, могу ли я рекомендовать использовать enum classes.

Смежные вопросы