Reputation: 9173
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:
void*
parameter. In this way, every message handler syntax will be void (*)(void*)
and then we will be able to use it as value of map.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:
&
as map value valid when map is const
? I know it is not when map itself is not const
but I wonder if it correct in this case or not.void(*)(void*)
for callback), but I like to know what do you think about it.Upvotes: 0
Views: 882
Reputation: 317
I think you can completely skip the base class. You just store the function pointer directly as some function pointer for the round trip conversion. I also made it accept many parameters:
#include <unordered_map>
#include <iostream>
#include <cassert>
struct Handler
{
template <typename T>
Handler(T fn)
: f((void(*)())(fn))
, info(typeid(T))
{
}
template <typename... Args>
void operator()(Args&&... args)
{
using Fn = void(Args...);
assert(info.hash_code() == typeid(Fn*).hash_code());
return ((Fn*)(f))(std::forward<Args>(args)...);
}
void (*f)();
const std::type_info& info;
};
int main()
{
std::unordered_map<int, Handler> cbmap;
cbmap.emplace(1, +[](int a, double b){std::cout << "1" << a << " " << b << "\n";});
cbmap.emplace(2, +[](double a){std::cout << "2" << a << "\n";});
cbmap.emplace(3, +[](double& a){std::cout << "3 " << a << "\n";});
double x = 42.0;
cbmap.at(1)(42,4.2);
cbmap.at(2)(4.2);
cbmap.at(3)(x);
}
Upvotes: 2