【发布时间】:2022-01-23 03:25:31
【问题描述】:
我正在尝试在地图中存储事件的回调。由于每个类应该有一个回调,我目前使用typeid(<eventClass>).name() 作为键,使用 std::function 作为值。我遇到的问题是在注册回调时,我需要事件的类型,而我想出的唯一可能的解决方案是将事件类作为模板参数传递。这是一个我想“优化”的工作示例:
std::map<Widget *, std::map<std::string, std::function<void(Widget*, Event &)>>> handlers;
template<typename T, std::enable_if_t<std::is_base_of_v<Event, T>, bool> = true>
void setCallback(Widget *obj, const std::function<void(Widget *, T &event)> &callback) {
handlers[obj].insert(std::make_pair(typeid(T).name(), reinterpret_cast<const std::function<void(Widget *, Event &)> &>(callback)));
}
void fire(Widget *obj, Event &event) {
auto it = handlers.find(obj);
if (it != handlers.end()) {
auto it2 = it->second.find(typeid(event).name());
if (it2 != it->second.end()) {
it2->second(obj, event);
return; // debug
}
}
printf("No handler found!\n"); // debug
}
由于setCallback函数中的模板参数,使用方法如下:
void callback(Widget *, BarEvent &barEvent) {
printf("%d\n", barEvent.getFoo());
}
void callback2(Widget *, FooEvent &fooEvent) {
printf("%d\n", fooEvent.getFoo());
}
int main() {
Widget *obj = new Widget();
Widget *obj2 = new Widget();
setCallback<BarEvent>(obj, callback);
setCallback<FooEvent>(obj2, callback2);
BarEvent event;
fire(obj, event);
fire(obj2, event);
FooEvent event2;
fire(obj, event2);
fire(obj2, event2);
delete obj;
delete obj2;
return 0;
}
在设置回调时必须传递模板参数很容易出错并且只是“开销”,因为事件类已经在回调中。有没有办法在setCallback函数中获取callback参数的第二个参数的类型?
如果可能,函数应该如下所示:
void setCallback(Widget *widget, const std::function<void(Widget *, Event &)> &callback) {
handlers[widget].insert(std::make_pair(typeid(<2nd param of callback>).name(), callback));
}
【问题讨论】:
-
reinterpret_cast不是你的朋友。如果我是你,我会问如何摆脱它,而不是如何将它包裹在闪亮的包装中。 -
啊,我的错。由于回调现在实际上将事件引用作为第二个参数而不是“T”,因此不再需要 reinterpret_cast
-
如果一切都是
std::function<void(Widget *, Event &)>,那么setCallback应该如何判断类型? -
如果实际传递的参数是
std::function<void(Widget*, FooEvent &>,那么 typeid 是否仍然有效,因为它是在运行时评估的? -
那么你打算如何从
std::function<void(Widget*, FooEvent &)>fostd::function<void(Widget*, Event &)>去?
标签: c++ templates std-function