2016-05-16 11 views
1

Dies ist die Fortsetzung this Frage.Vorlagenhandler Methoden Dispatcher

ich verschiedene Template-Methoden haben, die jeweils für eine andere Art von Nachricht, Nicht-Typ Template-Parameter und Template-Spezialisierung mit:

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"; 
} 

Nun, würde Ich mag eine andere Methode haben, um zu versenden der richtige Handler. So etwas wie

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

    } 
} 

die wie

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

So, wie diese Dispatcher erreicht werden könnte?

PS: Der vorherige Code nicht auf handle_message<T>(); wegen

Anmerkung: Vorlage Argument Abzug/Substitution fehlgeschlagen:

sollte nicht aufgerufen werden, die Standard-Handler?

+4

Welche Funktion aufgerufen wird, ist eine * Kompilierzeit * Entscheidung, aber es sieht so aus, als ob Ihr 'i' Wert bis * Laufzeit * nicht bekannt sein könnte. – aschepler

+0

@aschepler Ja, ich mische Laufzeit und kompiliere Zeit. Also, ich bin mit einer Switch-ähnlichen Struktur übrig, um zu entscheiden, welche Methode ich anrufen soll, sobald ich die Message ID (i) kenne? – perencia

+1

Sie übergeben einen Typ an eine Vorlage, die einen Aufzählungswert erwartet. Es ist nicht verwunderlich, dass es darum gekämpft hat, das Argument abzuleiten. –

Antwort

1

Ihre Methode sollte etwas sein wie:

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

Wenn Sie auf der Switch-Anweisung nicht selbst schreiben, können Sie eine Iteration bei der Kompilierung erzeugen unter Verwendung von z.B. boost :: 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

Dieses Beispiel vergleicht den Laufzeitwert mit jedem der ENUM-Werten und führt die passende Spezialisierung, wenn es vorhanden ist. Dies könnte durch die Verwendung einer binären Suche optimiert werden, Rückkehr einmal -Werten wurde gefunden, usw.

+0

Sehr interessant! – perencia

1

Noch eine weitere mögliche Implementierung ...

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

Und dann, wenn Sie Ihren Handler rufen für einen Wert wollen von 'i' ...

Aber das beste Ding, IMO zu tun, soll eine Schalteraussage verwenden. Wenn Sie Enums verwenden möchten, empfehle ich Ihnen, enum classes zu verwenden.