I'm trying to create a generalized a message handling a my code. Each message is identified by a integer id. Since all message handlers have similar deceleration and I like to handle each message quickly, I use a std::map
to connect and find corresponding message handler for specific message ids. Then I call this handler and pass message to it. There are several was to do this and here is an example:
const std::map<int, void(*)(void*)> g_handlers = {
{1, h1},
{2, h2}
};
...
// message
int message_id = 2;
int data = 3;
// handle message
g_handlers[message_id](&data);
But there are few big limitation for this method:
- Since there are different messages, we need to generalize them by passing them as
void*
parameter. In this way, every message handler syntax will bevoid (*)(void*)
and then we will be able to use it as value of map. - There is no type checking for this message. If someone incorrectly add message handler of message id 1 for message id 2, we may not find this bug quickly.
I wanted to try something new, so I was trying to find a way to solve these problems and I have finally reached a working code. Here is the code:
class handler_base {
public:
template <typename U>
void operator()(U* arg) {
run(arg, typeid(U));
}
private:
virtual void run(void* arg, const std::type_info& info) {}
};
template<typename T>
class handler : public handler_base {
public:
using type = T;
handler(void (*f)(T*)) :func(f) {
}
private:
void run(void* arg, const std::type_info& info) {
assert(info.hash_code() == typeid(T).hash_code());
func(static_cast<T*>(arg));
}
void (*func)(T*);
};
int main()
{
// 2 different types of handlers
handler h1(+[](double* v){ std::cout << "double called " << *v << "\n"; });
handler h2(+[](int* v){ std::cout << "int called " << *v << "\n"; });
const std::map<int, handler_base&> myhandler = {
{1, h1},
{2, h2}
};
double d = 1.5;
int i = 3;
myhandler.at(1)(&d);
//myhandler.at(1)(&i); // Error: failed assert due to type check
//myhandler.at(2)(&d); // Error: failed assert due to type check
myhandler.at(2)(&i);
}
Now here are my question:
- Is using
&
as map value valid when map isconst
? I know it is not when map itself is notconst
but I wonder if it correct in this case or not. - Is there any way simpler way to do this? providing different callback message handler syntax using same container with type checking?
- What do you think about this idea generally? Is it a good idea to add this complexity for type checking and heterogeneous callbacks? I personally always go for this rule of "simplicity is the best" and I normally select first approach (using generalized
void(*)(void*)
for callback), but I like to know what do you think about it.