通用C ++回调映射,还有更好的方法吗? [英] Generic C++ callback map, Is there any better way?
问题描述
我正在尝试创建一条通用消息来处理我的代码.每条消息均由整数ID标识.由于所有消息处理程序都有类似的减速度,并且我想快速处理每条消息,因此我使用 std :: map
连接并查找特定消息ID的相应消息处理程序.然后,我将其称为处理程序,并向其传递消息.有几种方法可以做到这一点,下面是一个示例:
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 *
参数传递来对其进行概括.这样,每种消息处理程序语法都将是void(*)(void *)
,然后我们就可以将其用作map的值. - 此消息没有类型检查.如果有人错误地为消息ID 2添加了消息ID 1的消息处理程序,则我们可能不会很快发现此错误.
- 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);
}
现在这是我的问题:
- 当地图为
const
时,使用&
作为地图值是否有效?我不知道map本身不是const
时不是,但是我想知道在这种情况下它是否正确. - 有没有更简单的方法来做到这一点?使用具有类型检查功能的同一容器提供不同的回调消息处理程序语法?
- 您总体上对这个想法有何看法?为类型检查和异构回调增加这种复杂性是一个好主意吗?我个人总是遵循"最简单是最好的"的规则,我通常选择第一种方法(使用通用的
void(*)(void *)
进行回调),但是我想知道您对此有何看法.
- 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.
推荐答案
我认为您可以完全跳过基类.您只需将函数指针直接存储为往返转换的某些函数指针即可.我还使它接受了许多参数:
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);
}
这篇关于通用C ++回调映射,还有更好的方法吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!