我试着为GLFW/Glad库编写一个事件分派程序。然而,我不太习惯于编写高效的代码(我一生的大部分时间都是用C#编写代码,而性能从来都不是我在大学里学到的)。
代码可以工作,但我不确定它是太复杂了还是太愚蠢了。我将感谢任何和所有的反馈。
我意识到的一个问题是,要从事件中获取任何有用的信息,就必须对事件进行dynamic_cast,这是不可取的。如果很少调用所有事件,那么dynamic_cast不会成为一个问题,但是这个调度程序也会处理关键的新闻事件,我认为每个帧转换几次将是一场灾难。此外,由于回调的注册方式,目前无法注销/分离回调。
我也没有真正想过内联,这让我有点困惑,因为你最终永远不会在内联的事情上有发言权。
#ifndef MYTH_FRAMEWORK_DISPATCHER
#define MYTH_FRAMEWORK_DISPATCHER
#include "mpch.h"
#include "MythEvent.h"
namespace MythFramework
{
using MythCallback = std::function<void(MythEvent&)>;
class MythDispatcher
{
public:
static void RegisterCallback(EventType eventType, MythCallback callback);
private:
MythDispatcher();
static MythDispatcher& GetInstance();
MythDispatcher(const MythDispatcher&) = delete;
MythDispatcher& operator=(const MythDispatcher&) = delete;
static void Trigger(EventType eventType, MythEvent& eventToDispatch);
std::map<EventType, std::vector<MythCallback> > callbacks;
friend class MythWindow;
friend class MythWinWindow;
};
}
#endif#include "MythDispatcher.h"
namespace MythFramework
{
MythDispatcher::MythDispatcher()
{
callbacks = std::map<EventType, std::vector<MythCallback> >();
}
void MythDispatcher::RegisterCallback(EventType eventType, MythCallback callback)
{
if (GetInstance().callbacks.find(eventType) == GetInstance().callbacks.end())
{
GetInstance().callbacks[eventType] = std::vector<MythCallback>();
}
GetInstance().callbacks[eventType].push_back(callback);
}
MythDispatcher& MythDispatcher::GetInstance()
{
static std::unique_ptr<MythDispatcher> instance;
if (!instance)
{
instance.reset(new MythDispatcher());
}
return *instance;
}
void MythDispatcher::Trigger(EventType eventType, MythEvent& eventToDispatch)
{
if (GetInstance().callbacks.find(eventType) == GetInstance().callbacks.end())
return;
//Could use auto but i hate auto
for (std::vector<MythCallback>::iterator it = GetInstance().callbacks[eventType].begin(); it != GetInstance().callbacks[eventType].end(); it++)
{
(*it)(eventToDispatch);
}
}
}从window类中提取以显示如何调用事件:
/\* ###################### GLFW CALLBACKS ###################### \*/ glfwSetWindowFocusCallback(window, [](GLFWwindow\* window, int focus) { if (focus == GL\_TRUE) { MythDispatcher::Trigger(EventType::AppFocus, MythFocusEvent()); } else { MythDispatcher::Trigger(EventType::AppLostFocus, MythLostFocusEvent()); } }); glfwSetWindowPosCallback(window, [](GLFWwindow\* window, int x, int y) { MythDispatcher::Trigger(EventType::AppMove, MythMoveEvent(x, y)); }); glfwSetWindowSizeCallback(window, [](GLFWwindow\* window, int width, int height) { MythDispatcher::Trigger(EventType::AppResize, MythResizeEvent(width, height)); });
从MythApp.h中提取,显示如何注册回调:
MythDispatcher::RegisterCallback(EventType::AppExit, [this](MythEvent& e) { running = false; });
编辑:Github回购
发布于 2022-01-20 07:52:34
我们在头文件中缺少了一些内容:
#include <functional>
#include <map>
#include <vector>在执行过程中:
#include <memory>我不是单例的粉丝,这很难独立进行单元测试。
然而,如果这是一个已完成的交易,我不认为有任何调用,例如方法和数据-它可以只是一个静态的类。
成员应该在构造函数的初始化列表中初始化,而不是在主体中初始化:
MythDispatcher::MythDispatcher()
: callbacks{}
{}更好的是,我们可以在类定义中进行内联初始化,从而允许我们默认构造函数:
private:
std::map<EventType, std::vector<MythCallback> > callbacks = {};
MythDispatcher() = default;公开Trigger()可能更好,所以我们不必列出所有可能使用它作为朋友的类。这是一个潜在的开放式集合,这使得我们很难预见每一个用例。
在RegisterCallback()中,我们默认地构造一个新的映射条目,如果它还不存在的话。但是这正是std::map::operator[]已经做的,所以我们可以这样写它:
void MythDispatcher::RegisterCallback(EventType eventType, MythCallback callback)
{
GetInstance().callbacks[eventType].push_back(std::move(callback));
}在GetInstance()中,我们从instance == nullptr开始。但这不是必要的,因为我们可以用实际的对象初始化--它将在第一次调用函数时填充。我们甚至不需要智能指针,因为它在程序结束之前不会被删除:
在Trigger()中,我们真的应该克服你对auto的厌恶。这种突出的类型(和重复查找)使得行长,充满了杂乱:
//Could use auto but i hate auto for (std::vector<MythCallback>::iterator it = GetInstance().callbacks[eventType].begin(); it != GetInstance().callbacks[eventType].end(); it++) { (\*it)(eventToDispatch); }
听着,要清楚得多:
for (auto& callback: GetInstance().callbacks[eventType]) {
callback(eventToDispatch);
}我也避免使用多个GetInstance()和callbacks[eventType]查询:
void MythDispatcher::Trigger(EventType eventType, MythEvent& eventToDispatch)
{
auto const& callbacks = GetInstance().callbacks;
auto const it = callbacks.find(eventType);
if (it == callbacks.end()) {
return;
}
for (auto& callback: it->second) {
callback(eventToDispatch);
}
}我已经更改了接口,这样它就不再是单例了--因此,如果需要的话,应用程序可以使用多个分派器(单元测试当然需要)。
#include <functional>
#include <map>
#include <vector>
namespace MythFramework
{
using MythCallback = std::function<void(MythEvent&)>;
class MythDispatcher
{
std::map<EventType, std::vector<MythCallback>> callbacks = {};
public:
MythDispatcher(const MythDispatcher&) = delete;
MythDispatcher& operator=(const MythDispatcher&) = delete;
void RegisterCallback(EventType eventType, MythCallback callback);
void Trigger(EventType eventType, MythEvent& eventToDispatch);
};
}#include <utility>
namespace MythFramework
{
void MythDispatcher::RegisterCallback(EventType eventType, MythCallback callback)
{
callbacks[eventType].push_back(std::move(callback));
}
void MythDispatcher::Trigger(EventType eventType, MythEvent& eventToDispatch)
{
auto const it = callbacks.find(eventType);
if (it == callbacks.end()) {
return;
}
for (auto& callback: it->second) {
callback(eventToDispatch);
}
}
}Qt库的核心是一个事件调度器,经过了几十年的战斗测试。值得一看它是如何运行的,因此您可以看到在您自己的实现中应该解决哪些问题。
发布于 2022-01-20 11:55:25
除了托比·斯皮特的出色回答:
添加的每一种类型都以Myth开头。但是,一旦将所有内容都放在名称空间中,就没有理由重复名称空间名称的部分。我还建议将MythFramework重命名为Myth。然后,它变成:
namespace Myth
{
using Callback = std::function<void(Event&)>;
class Dispatcher {
...
};
};另外,什么是MythWinWindow?
你写了这段代码:
//Could use auto but i hate auto
for (std::vector<MythCallback>::iterator it = GetInstance().callbacks[eventType].begin(); it != GetInstance().callbacks[eventType].end(); it++)
{
(*it)(eventToDispatch);
}您不需要auto将其转换为基于范围的循环:
for (MythCallback &callback: callbacks[eventType])
{
callback(eventToDispatch);
}但请不要讨厌这些东西。您可能不喜欢auto的某些方面,但这并不意味着auto也有可能超过某些场景中的缺点的优点。“讨厌”只会使您更难看到这些好处,如果您不在适当的地方使用auto,您的代码质量可能会受到影响。也许它甚至使你看不见没有auto还可以使用范围的事实?
std::unordered_map您不需要callbacks按任何特定的顺序排序,所以可以考虑使用std::unordered_map,因为它比std::map具有更快的查找速度。
请注意,您甚至可以避免使用(无序的)映射,方法是让MythDispatcher成为模板,而事件类型是模板参数。基本上,每个事件类型都有一个分配器。
https://codereview.stackexchange.com/questions/273162
复制相似问题