Basic Event-Based Programming

Dale Smith By Dale Smith
Expert Author
Article Date:

One of the largest hurdles I have found both myself and others struggle with is learning to think in different ways to accomplish a task. We naturally try to do things in a systematic, linear way.

This is all well and good when telling a story, or making your plans for the day, or choosing what order to get things in the supermarket. But there is another way we think subconsciously- an event-based way, or reacting to the other things happening around us. We usually don’t realize we’re doing it, but it would be a bad day if we didn’t slow down at the intersection in our vehicles, or step over that wire dangling haphazardly across the office. Wouldn’t it be great if our programs could respond like that too?

One of my first forays into Object-Oriented Programming (OOP) was writing a very small, very basic game engine with Python, using the Pygame library. It wasn’t much- drawing the game board and player, with some arrow key movement. But it opened my eyes to a whole new method of writing programs, and understanding the why behind OOP, where with all the sequential programming we had done in my college classes, I didn’t understand the point.

The basic idea is this: Your program’s entry point will setup your base objects, probably some kind of managers. If we were writing a game engine for instance, we might initialize the screen, and user input devices. Then we start the ‘loop’, where on every clock cycle we would check all of our objects for tasks they need to do based on what’s happened in that clock cycle. So say the player in our game engine clicks the start button, in the code beneath our mouse manager would send the click event with coordinates to the main event manager. This event would then be re-broadcast to the rest of the resource management objects to do their own tasks. The UI manager would use the coordinates and find that the start button was clicked, firing an event that causes the game state to change to playing, prompting the screen manager to draw the game… you get the idea.

So how do we setup such a system? Luckily I have some code handy to outline a very basic example.

This is C++ compiled working on Ubuntu Linux 11.10 with g++. For ease of display on this site, I have compiled all the classes into one file. As the classes expand, splitting them up into .h and .cpp files is a must.


//First our basic includes
#include <iostream>
#include <string>
#include <vector>

using namespace std;

//Define our base classes that everything else will inherit from

//Base Event class - this is needed so we can send many types of events to all objects
class Event {
public:
Event() {
this->type = "Generic Event";
}
virtual string getType() {
return this->type;
}
private:
string type;
};

//Base Object class - this will parent our manager objects so we can store them in a vector together
class Object {
public:
virtual void recv(Event *event){};
};

//Event Manager: This is where the magic happens
class EventManager : public Object {
public:
//When objects are initialized they will be passed the EventManager object's pointer. They then
//call registerObject with themselves as the parameter
int registerObject(Object *object) {
this->objects.push_back(object);
}
//When something happens, an Object will call broadcast with the appropriate event, which will
//then be sent to all other objects via a recv() method.
void broadcast(Event *event) {
for(int i = 0; i < this->objects.size(); i++) {
this->objects[i]->recv(event);
}
}
private:
vector<Object*> objects;
};

//This event will be fired each clock cycle - this acts as a 'ping' to objects like a mouse or keyboard
//manager so they can check if any input is pending
class TickEvent : public Event {
public:
TickEvent() {
this->type = "Tick Event";
}
string getType() {
return this->type;
}
private:
string type;
};

//This is our 'Ticker', the object that will start the whole thing
class Ticker : public Object {
public:
Ticker(EventManager *EM) : Object() {
this->em = EM;
//Here you can see the Ticker object being registered with the Event Manager
this->em->registerObject(this);
}
//Once everything is setup in our main, we call run() to start the process
void run() {
while(true) {
//On each loop we send a tick event to the event manager
this->em->broadcast(new TickEvent());
}
}
//A basic example of a registered object receiving events- here we parse out tick events and
//could do different things depending on which event we actually do get.
void recv(Event *event) {
if((*event).getType() != "Tick Event") {
//Do something on events other than ticks
}
}
private:
EventManager *em;
};

//This is an optional class I like to add that we can later disable for real-time debugging
class Debugger : public Object {
public:
Debugger(EventManager *em) : Object() {
this->em = em;
this->em->registerObject(this);
}
//For now we simply cout the name of events as they come in- without parsing the tick events
//out, we see a LOT of ticks. Once more objects have been added we would watch for specific
//Events instead of catching them all
void recv(Event *event) {
cout << (*event).getType() << endl;
}
private:
EventManager *em;
};

//And finally, our entry point. We setup the main objects and run the program.
int main(int argc, char** argv) {
EventManager* EM = new EventManager();
Ticker* CPU = new Ticker(EM);
Debugger* Bug = new Debugger(EM);

CPU->run();
return 0;
}

So there it is in a nutshell, this could easily be separated into multiple class files and be used in a real application with the help of a UI library, and some input. But that is beyond the scope of this article. Maybe next time.

About Dale Smith
Dale is part of the IT team at iEntry Inc.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • 160×600
  • 152X252
  • Newsletter Signup
    Get The Email Newsletter! Please subscribe using your company email address
  • 336×280