gpt4 book ai didi

C++ 事件系统 - 多态事件和事件处理程序

转载 作者:行者123 更新时间:2023-12-01 14:48:54 27 4
gpt4 key购买 nike

我已经编写了我能想到的最基本的事件系统。我来自 javascript 背景,所以我关注了 On , Off , Emit句法。目的是能够创建一个 EventSystem可以Emit任何类型的派生Event对象并调用适当的处理程序。

请注意,由于 的原因,我被困在 C++98 中

到目前为止,我最好的想法是有一个简单的 Event对象和每个 Event 的 typedef类型来处理它。

class Event {};
class AlarmEvent : Event {};
class ErrorEvent : Event {};

typedef void (*EventHandler)(Event event);
typedef void (*AlarmEventHandler)(AlarmEvent event);
typedef void (*ErrorEventHandler)(ErrorEvent event);

我的问题是我希望我的模块能够尽可能容易地附加。
int main()
{
Module module;

EventSystem es;
Event shutdown_event("shutdown");
AlarmEvent alarm_event("alarm", "Oh crap");

es.On("shutdown", module.OnEvent);
es.On("shutdown", module.OnEvent);
es.On("alarm", module.OnAlarmEvent);

es.Emit(shutdown_event);
es.Emit(alarm_event);
}

但是看着 EventSystem
class EventSystem {
public:
void On(std::string id, EventHandler handler);
void Emit(Event event);
void GetEventHandlers(std::string id, std::vector<EventHandler> *&handlers);
std::map<std::string, std::vector<EventHandler> > events;
};

我需要一个 On , GetEventHandlers , 和 events每个事件类型的属性。这很快就会变得很糟糕。有没有更好的路径,我可以使用模板来允许 EventSystem保持尽可能简单?

最佳答案

C++98 是旧的,比可变参数模板更早。下面模拟带有链表的可变参数模板,这是非常次优的,但它应该可以工作。

// linked lists for "variadic" templates
struct Nil { };
template<typename X, typename XS>
struct Cons { };

// utility type
struct BlackHole {
template<typename T>
BlackHole(const T&) { }
};
// anything can be converted to a BlackHole implicitly, but it's a "worse"
// conversion than being converted to a base class

// I would template your event system over every event type
// this implementation only works properly if more derived events appear before their bases
template<typename Events> // e.g. Events = Cons<AlarmEvent, Cons<ErrorEvent, Cons<Event, Nil>>>
class EventSystem;

template<>
class EventSystem<Nil> {
protected:
// see below for Emit/EmitEmitted thing
// usage of BlackHole means that e.g. if calling with AlarmEvent
// and only overloads for Event and BlackHole are visible
// then the former will be chosen, since the latter conversion is worse
// can't just say template<typename T> EmitEmitted(T const&) { }
void EmitEmitted(BlackHole) { }

public:
// these overloads exist so the using declarations ahead don't fail
// for maximum type-safety, create a private type and
// make it an argument of each, so they can never be called
// using Emit/EmitEmitted creates type safety; again, see below
void Emit() { }
// On has easy type safety: you just can't call it for an unknown type
void On() { }
// GetEventHandlers doesn't really make sense anyway
// I don't think you need it, you can't have a vector of mixed handlers
// so why bother?
};
template<typename X, typename XS>
class EventSystem<Cons<X, XS> > : public EventSystem<XS> {
std::vector<void (*)(X)> handlers;

protected:
// "forward" all the EmitEmitted overloads made for XS
using EventSystem<XS>::EmitEmitted;
// overload for the specific case of an X
void EmitEmitted(X x) {
// fire all of the X-specific handlers
for(typename std::vector<void (*)(X)>::iterator i = handlers.begin(); i != handlers.end(); ++i) {
(*i)(x);
}
// call the rest of the handlers
EventSystem<XS>::EmitEmitted(x);
}

public:
// more "forwarding"
using EventSystem<XS>::Emit;
void Emit(X x) {
return EmitEmitted(x);
}
// suppose you have an EventSystem<Cons<std::string, Nil> >
// if you Emit an int, say, then you want this to fail
// thus the overload of Emit in EventSystem<Nil> should not be
// a catch-all or anything
// however, if you emit a std::string, then you need to recursively
// emit from EventSystem<Nil>, to handle any handlers for superclasses
// now you don't want it to explode
// solution? two functions
// Emit is the public entry point, and fails on unknown types
// EmitEmitted is named so because, once it's called, the type
// is known to be known, and will/has been emitted by at least one layer
// it no-ops once the base case is reached
// it is protected, and it is where the actual logic is

// easy now, right?
using EventSystem<XS>::On;
void On(void (*handler)(X)) {
handlers.push_back(handler);
}
};

示例用法:
struct Event {
std::string message;
Event(std::string message) : message(message) { }
};
void HandleEvent(Event e) {
std::cerr << e.message << "\n";
}
class AlarmEvent : public Event {
int hour;
int minute;
static std::string BuildMessage(int hour, int minute) {
std::stringstream builder;
builder << "Alarm: " << std::setfill('0');
builder << std::setw(2) << hour << ":";
builder << std::setw(2) << minute;
return builder.str();
}
friend void HandleAlarm(AlarmEvent);

public:
AlarmEvent(int hour, int minute) : Event(BuildMessage(hour, minute)), hour(hour), minute(minute) { }
};
void HandleAlarm(AlarmEvent a) {
// please ignore the fact that this is very stupid
if((a.hour + (a.minute / 60)) % 24 < 12) std::cerr << "AM Alarm\n";
else std::cerr << "PM Alarm\n";
}
struct ErrorEvent : Event {
ErrorEvent(std::string message) : Event(message) { }
};
void HandleError(ErrorEvent) {
static int count = 1;
std::cerr << "Error " << count++ << "\n";
}

int main() {
EventSystem<Cons<AlarmEvent, Cons<ErrorEvent, Cons<Event, Nil> > > > system;
// all handled by overload resolution
// no need to say what type you're dealing with
system.On(HandleEvent);
system.On(HandleAlarm);
system.On(HandleError);
// doesn't work
// system.On(std::exit)

system.Emit(ErrorEvent("Bad things"));
system.Emit(AlarmEvent(2, 30));
system.Emit(Event("Something happened"));
system.Emit(ErrorEvent("More bad things"));
system.Emit(AlarmEvent(11, 67));
// doesn't work
// system.Emit(5);
}

不确定所有示例代码都是 C++98,但这没关系。 It appears to work nicely .此外,这里有很多复制。建议从 void (*)(T) 更改处理程序(需要拷贝)到 void (*)(T&)void (*)(T const&) .

关于C++ 事件系统 - 多态事件和事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59816658/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com