1

This is the continuation of this question.

I have different template methods, each one for a different kind of message, using non-type template parameters and template specialization:

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

Now, I'd like to have some other method to dispatch to the correct handler. Something like

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

    }
}

which could be invoked like

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

So, how this dispatcher could be accomplished ?

PS: The previous code fails on handle_message<T>(); because of

note: template argument deduction/substitution failed:

shouldn't the default handler be invoked ?

Community
  • 1
  • 1
perencia
  • 1,498
  • 3
  • 11
  • 19
  • 4
    What function to call is a *compile-time* decision, but it looks like your `i` value may not be known until *run-time*. – aschepler May 16 '16 at 20:49
  • @aschepler Yes, I'm mixing runtime and compile time. So, i'm left with a switch-like structure to decide which method to call once i know the message id (i) ? – perencia May 16 '16 at 20:53
  • 1
    You're passing a type to a template expecting an enum value; it's not surprising it struggled to deduce the argument. – Alan Stokes May 16 '16 at 20:53

3 Answers3

1

Your method should be something like:

void handle(MSG_TYPES type) {
    switch (type) {
        case MSG1: handle_message<MSG1>();
        case MSG2: handle_message<MSG2>();
    }
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

If you do not want to write the switch statement yourself, you can generate an iteration at compile time using e.g. 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

This example compares the runtime value with each of the enum values and executes the matching specialization if it exists. This could be optimized by using a binary search, returning once a values was found, etc.

m.s.
  • 16,063
  • 7
  • 53
  • 88
1

Yet another possible implementation...

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

And then, when you want to call your handler for a value of 'i'...

handlers[i]();

But the best thing to do IMO is to use a switch statement. Also, if you're going to use enums, might I recommend you use enum classes.

Community
  • 1
  • 1
Altainia
  • 1,347
  • 1
  • 7
  • 11