Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 更好的事件处理机制?_C++_Events_Event Handling - Fatal编程技术网

C++ 更好的事件处理机制?

C++ 更好的事件处理机制?,c++,events,event-handling,C++,Events,Event Handling,我正在为GUI编写一个事件系统,其中有一个事件基类和两个派生类(例如MouseDownEvent,MouseUpEvent,…等等) 每个GUI元素为它应该/想要处理的每种类型的事件注册一个回调 下面是“典型”回调的外观: bool-OnMouseMove(const-MouseMoveEvent&event); 事件处理函数如下所示: template<typename T, typename Callback> bool Dispatch(Callback&&

我正在为GUI编写一个事件系统,其中有一个
事件
基类和两个派生类(例如
MouseDownEvent
MouseUpEvent
,…等等)

每个GUI元素为它应该/想要处理的每种类型的事件注册一个回调

下面是“典型”回调的外观:

bool-OnMouseMove(const-MouseMoveEvent&event);
事件处理函数如下所示:

template<typename T, typename Callback>
bool Dispatch(Callback&& callback)
{
    try
    {
        return callback(dynamic_cast<T&>(m_Event));
    }
    catch (const std::bad_cast&)
    {
        return false;
    }
}
bool-OnEvent(事件和事件)
{
事件dispatcher dispatcher(事件);
dispatcher.Dispatch(/*std::绑定回调*/);
/* ... */
}
Dispatch
看起来是这样的:

template<typename T, typename Callback>
bool Dispatch(Callback&& callback)
{
    try
    {
        return callback(dynamic_cast<T&>(m_Event));
    }
    catch (const std::bad_cast&)
    {
        return false;
    }
}
模板
布尔调度(回调和回调)
{
尝试
{
返回回调(动态_cast(m_事件));
}
捕获(常量标准::错误的\u投射&)
{
返回false;
}
}
所以我的问题是
调度
功能中的
动态\u cast
ing,同样根据答案,如果我必须做这种“变通”,那么系统中有一个设计流程,我应该重新考虑它,而不是尝试修补它


是否有更好的方法来处理此问题?

要对其进行一般性的框架设置,您有一组类型已擦除的对象(元素、事件),并且需要为两种类型的特定对(例如
ElementA
MouseUpEvent
)分派处理程序。这是一个问题,在C++中有两种处理方法。 使用访问者模式进行双重分派 这是书中描述的“经典”技巧。基本上,基本事件(节点)有一个虚拟方法,该方法将调度到元素(访问者)中的降级调度方法。visitor方法是分派到正确元素的虚拟方法。你可以在书上、杂志上或报纸上读到。我在这里没有更好的教程,只是补充说,通过使用可变模板,您可以大大减少样板文件的数量:

//访问者
模板
结构GenericVisitor;
模板
结构GenericVisitor:公共GenericVisitor{
虚拟布尔访问(consttnode&{return false;}
};
模板
结构GenericVisitor{
virtual~GenericVisitor()=默认值;
};
//节点
模板
结构GenericBaseNode{
虚拟布尔接受(TVisitor和visitor)常量=0;
};
模板
结构GenericNode:公共GenericBaseNode{
虚拟bool Accept(TVisitor和visitor)const{return visitor.Visit(*static_cast(this));}
};
之后,只需几行代码即可为您的设置添加细节:

struct MouseEvent;
结构键事件;
使用元素=GenericVisitor;
使用Event=GenericBaseNode;
struct MouseEvent:public GenericNode{};
structKeyEvent:publicGenericNode{};
结构元素A:公共元素{
虚拟布尔访问(const MouseEvent&{return true;}
};
要分派,只需调用事件的
Accept
方法:

void DispatchEvent(事件和事件、元素和元素){
事件。接受(元素);
}
使用标记的联合进行双重分派(std::variant) 由于C++17(或带有a的C++11),您可以使用一个名为的类型安全联合,它可以在同一内存空间中保存一组固定的类型,并在它们之间进行调度。调用的全局方法可用于根据包含的一个或多个变量类型分派回调。这对于双重分派来说非常方便

首先,使用变体来保存事件:

struct MouseEvent{};
结构键事件{};
使用Event=std::variant;
然后,为每个元素提供一个变量,以及处理每个事件的方法(使用一个基类来除去一些锅炉板):

struct BaseElement{
模板
bool-OnEvent(const-TEvent&){return-false;}
};
结构元素A:公共基本元素{
使用BaseElement::OnEvent;
bool-OnEvent(const-MouseEvent&){return-true;}
};
使用Element=std::variant;
然后使用带有通用lambda的
std::visit
发送:

void DispatchEvent(事件和事件、元素和元素){
std::visit([](自动和&el,自动和&ev){return el.OnEvent(ev);},元素,事件);
}
最终,它生成和优化的内容看起来有点像旧的C GUI应用程序的事件处理程序:

struct Event{int id;union{MouseEvent AsMouseEvent;KeyEvent AsKeyEvent;};};
结构元素{int id;并集{ElementA AsElementA;};};
开关(event.id){
case MouseEvent:
开关(元素id){
案例要素A:
返回元素.AsElementA.OnEvent(event.AsMouseEvent);
/*...*/
}
打破
/* ... */
}
选择 这两种方法在性能方面都没有明显的整体优势,因此我建议只使用看起来更易于使用或维护的方法

访问者模式
  • 随着类型的增加,访客的可变大小也会增加
  • 可与C++98一起使用
  • 异构容器必须存储到基类型的动态分配指针
  • 。。。但是没有浪费的对象内存
std::变体
  • 生成的分派代码的数量随着类型的增加而增加
  • c++11(通过库、带std的c++17)或更高版本
  • 可以在连续内存中存储异构成员
  • 。。。但如果对象大小不同,则会浪费内存

这两种类型的演示:

要对其进行一般性的框架设置,您有一组类型已擦除的对象(元素、事件),并且需要为两种类型的特定对(例如
ElementA
MouseUpEvent
)分派处理程序。这是一个问题,在C++中有两种处理方法。 使用访问者模式进行双重分派 这是书中描述的“经典”技巧。基本上,基本事件(节点)有一个虚拟方法,该方法将调度到元素(访问者)中的降级调度方法。visitor方法是分派给righ的虚拟方法