[Disclaimer: This is an experimental post, I would not actually completely condone using this; heck, I'm not even using this. I'm using a queue based event system with reference counting smart pointers.]
For the sake of experimenting, and to make a new and "safer" Event, I found out I could call a protected function from outside of the scope of that class, from using a function pointer assigned by the parent class. Certainly this doesn't sound safe, as it bypasses encapsulation measures; however, this allowed me to fire an event in a way only the parent could.
So I was trying to come up with an ideal Event for an EventSystem, and I had a couple ideas, though things bugged me in each. So, this is a typical Event, which I do have a slight problem with:
class Event
{
public:
void Attach(IEventObserver* eventObserver);
void Detach(IEventObserver* eventObserver);
void FireEvent(const EventArgs& eventArgs);
};
So, an IEventObserver can both register itself AND fire off an event; the IEventObserver shouldn't be able to fire the event it's registering to, should it? Only the parent that contains the Event should, in my belief. So I thought, is there any way I can allow the parent class to fire off the event and still allow the Event to be public? Okay, I figured I could setup accessors to the event; however, this meant I'd have to setup an accessor for each and every single event I have, which could get ugly fast.
I eventually thought of a sneaky way, that actually shouldn't even be possible. Giving the parent class, what I call the trigger, a function pointer to a protected function that fires the event. After writing it, I realized while I liked the idea, everything about coding the use of the Event was a lot more difficult, so I provided macros to do the dirty work for you; however, it still seems like an annoying setup to me. I usually code things to be easy to interact with, and what I came up with clearly goes against my programming paradigms.
So, here's the tiny change in the Event:
class Event
{
protected:
void FireEvent(const EventArgs& eventArgs);
public:
void Attach(IEventObserver* eventObserver);
void Detach(IEventObserver* eventObserver);
};
Obviously there isn't much of a difference here, there isn't much to add. The difference is here in the full version:
template <class IEventArgs>
class Event
{
typedef std::list<IEventObserver<EventArgs>*> EList;
protected:
EList _observers;
void FireEvent(IEventArgs& eventArgs)
{
EList::iterator iter = _observers.begin();
while(iter != _observers.end())
{
(*iter)->CatchEvent(eventArgs);
++iter;
}
}
public:
typedef void (Event<IEventArgs>::*IEventTrigger)(IEventArgs& eventArgs);//use an event trigger mechanism
Event(EventTrigger& trigger)
{
trigger = &Event::FireEvent;
}
virtual ~Event()
{
}
void Attach(IEventObserver<IEventArgs>* eventObserver)
{
_observers.push_back(eventObserver);
}
void Detach(IEventObserver<IEventArgs>* eventObserver)
{
_observers.remove(eventObserver);
}
};
Note the EventTrigger; to create an Event you have to pass in a reference to a function pointer. Here are the three Macros I have at the top of my Event.h file (they looked ugly and hard to read inside of the code block, so here they are in a pre tag):
#define EVENT_H(variable,argType) Event::EventTrigger trigger##variable; Event* variable;
#define EVENT_CPP(variable,argType) trigger##variable = NULL; variable = new Event(trigger##variable);
#define EVENT_FIRE(variable,args) ((variable)->*(trigger##variable))(args);
You place a macro call to EVENT_H inside of the .h file, a call to EVENT_CPP in the .cpp file where the event is being constructed, and EVENT_FIRE when you want to fire the event (though, EVENT_H and EVENT_CPP are actually more annoying to use than just setting up the Event by hand, so I'll probably remove those two macros, as they don't actually help at all).
So, ignoring the macros, lets say I want to create an event, I can do this.
Event::EventTrigger keyTrigger= NULL;
Event* keyEvent = new Event(keyTrigger);
This will create an Event for me to use as well as hook up a trigger to fire the event. When I finally want to fire the event I can do something like this;
(keyEvent->*(keyTrigger))(KeyEvent(FK_F11));//this event and argument are just for example
So, this system is what I came up with, though I'm still not so sure if I like it. I'd love to get any comments or suggestions for a better system. Even if I don't use this setup, I still found this process to be an interesting learning experience. I just wanted to find out if I could get access to a protected function using a function pointer; I found out I could, and I find that intriguing. For clarification, the Event in the above example would be public, and the trigger protected (or private); so I have to update my macro EVENT_H to include those overrides. I'll provide the source code in a couple days or so when I get everything properly packaged together; right now the code is sitting inside of my engine, and are tied into various systems, so the source files won't compile for you without modification (because I can't include all of the other misc. files). So, this still isn't a lot nicer than the other solution; it's merely replacing an accessor with a trigger. So, either way it's kind of ugly I think, to ensure safe firing of an event.
Anyway, I haven't touched Event Systems in more than a couple weeks, it's something I'll have to worry about very shortly; so when I revisit them, I'll probably make a blog post concerning my more finalized systems with what I finally decide to use. Any feedback would be greatly appreciated. Thanks!
I apologize if the post jumps around a bit, it is very late as I type this.
Comments