FurryHead

Basic C++ Event Handler Class If youre familiar with C# event handlers, this is a C++ mimic of

Recommended Posts

Hey everyone, I'm going to teach you how to make a simple event handler in C++.

If you happen to be familiar with C#'s event handler system, this is going to mimic that.

I got the idea from a tutorial on how to make this same class in Python, but I couldn't find anything comparable to it in C++. Plus, it might be useful to someone...

Anyway, on to the code. I would recommend basic knowledge of generic classes, operator overloading, and the concept of function pointers. If you don't happen to know these, I will offer a really basic explanation along the way.

Note: All of this will be done from within Code::Blocks, it should be relatively easy to follow along with any other environment though.

So, to start with we will create two files - One header for the event handler class, one for the test program. Name them 'event.h' and 'main.cpp'. I would use a separate file for the event handler declarations and definitions, but with generics you can't do that. Unlike with a non-generic class, it will not give you linkage errors if you put the definitions in the header because of the way it compiles.

Now, in order to keep track of all the listeners to add we will be using a vector of function pointers. More on this later. So we will add the following skeleton code into event.h:

#include <vector>#include <algorithm> // For the remove listener function so we can use std::remove_iftemplate <class Data> class Event {	typedef void (*fptr)(Data);	typename std::vector<fptr> listeners;	public:	// nothing yet!};

Alright, now let's go through this line by line (after the includes)

template <class Data> class Event {
This line says that this is a generic class, and to define "Data" as whatever type is passed to us when the user creates a instance of this class. So, if we made a instance of this class as "Event<string> myEventHandler;" it would define Data as type string. If you want more detailed information about generics, see a C++ book or a tutorial or google it.

typedef void (*fptr)(Data);
This (basically) defines the type "void (*)(Data)" to fptr, so when we use "fptr myPtr;" we are declaring (a lot easier) a pointer to a function returning void taking a parameter of type "Data".

typename std::vector<fptr> listeners;
Creates a vector of function pointers. The typename keyword is sometimes needed to tell the compiler that 'Data' in the fptr typedef is a template name, not a object name - big difference, especially since g++ will spew unreadable errors about it.

Well, we have a basic skeleton now. Now we will add the operators!
Add these two functions right after the public: line and before the last };
Event& operator+=(fptr func) {		return add(func);	}	Event& operator-=(fptr func) {		return sub(func);	}	void operator()(Data& eventData) {		changed(eventData);	}

Well, each of those are pretty much the same - they delegate to other functions.
Read any tutorial on operator overloading for information on the first two... But I must explain the third one a little.

The third one is really neat, it's called a "Functor" - basically, look at the following code:
Event handler; // assuming we have finished the classhandler.add(function_name); // These two lineshandler += function_name;   // have the exact same effect (the same applies to sub and -=)//here's the neat functorhandler("Some new data");handler.changed("Some new data");
The last two lines accomplish the exact same thing! I suppose this is why the docs call functors "Functions on steroids" ;) See google for more functor information.

Anyway, we now need to define the other 3 functions - add, sub, and changed. We'll start with changed():
void changed(Data& eventData) {	for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) {		(*iter)(eventData);	}}
Well, assuming you know at least the basics of C++ you will know what the first line does. I'll go over the other two lines, though:

for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) {
If you've ever iterated over a vector, it's the same except for the typename keyword. It tells the compiler that Data is a template name not a object name. But wait, where did we say Data in this line? Well, we didn't. We said it in the typedef for fptr; Therefore it implies that it's here as well. Try compiling it without the typename, and good luck trying to understand the errors.

(*iter)(eventData);
This is the interesting part. To call a function through it's pointer, you can either use "fptr func_ptr; func_ptr =...; func_ptr();" as if it's a normal function, but the more common use is "(*func_ptr)()" because it explicitly tells the reader that it's calling a pointer to a function. Since we are using a iterator, that adds another level of misdirection - Technically, we could also use "(**iter)(eventData)" because one * de-references the iter pointer to the original function pointer, then the second * de-references it to the function itself. In short, iter is now a pointer to a pointer to a function, and the function can be called as "(**iter)(eventData)" OR (*iter)(eventData)". Read up on function pointers for more details.

Now for the other two functions:
Event& add(fptr func) {	for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) {		if (*iter == func) // we must dereference the pointer-to-pointer-to-function to get the pointer-to-function so that we can compare it			// Case was true, the function has already been added so we return and don't add it again			return *this;	}	// Ok, func has not yet been added (or added then removed) so we can add it	listeners.push_back(func);	return *this;}// helper function for std::remove_ifbool evt_equal(fptr func1, fptr func2) {	return (func1 == func2);}Event& sub(fptr func) {	// This line removes all occurrences of func in the listeners list, if any.	listeners.erase(std::remove_if(listeners.begin(), listeners.end(), evt_equal), listeners.end());	return *this;}
Well, most of it is commented and explains itself, but I will talk about returning the "this" pointer for a moment.

The special keyword "this" is a pointer to the class instance that you're working from. When you say "return this;" it is returning a pointer - which is not good, as operators should return a reference to the object so that it is assignable to a non-pointer variable. So we de-reference the this pointer, thus returning the Event object in a way that is convenient to assign.

Now, all the code put together in event.h is as follows:
#include <vector>#include <algorithm>template<class Data> class Event {	typedef void (*fptr)(Data); 	std::vector<fptr> listeners;	public:	void changed(Data& eventData) {		for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) {			(*iter)(eventData);		}	}	Event& add(fptr func) {		for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) {			if (*iter == func)				return *this;		}		listeners.push_back(func);		return *this;	}	bool evt_equal(fptr func1, fptr func2) {		return (func1 == func2);	}	Event& sub(fptr func) {		listeners.erase(std::remove_if(listeners.begin(), listeners.end(), evt_equal), listeners.end());		return *this;	}	// operators	Event& operator+=(fptr func) {		return add(func);	}	Event& operator-=(fptr func) {		return sub(func);	}	void operator()(Data& eventData) {		changed(eventData);	}};
Looking good, eh? Well, now we can test it out!!! :lol:

Here's the code for main.cpp, the comments will explain it:
#include <string>#include <iostream>#include "event.h"using namespace std;void onEvent(string data) {	cout << "Handler function called with: " << data << endl;}void onEvent2(string data) {	cout << "Handler function number 2 called with: " << data << endl;}int main() {	Event<string> handler; // Create an instance of Event, passing the string type as "Data" in the class	handler.add(onEvent); // Pass the onEvent function to the Event class, telling it to notify the 								   // function when something changes. also same as handler += onEvent;	string msg = "My handler works!"; // make a message	handler.changed(msg); // tell the event class that something changed, including the data - 									// update all the listener functions! also same as handler(msg);	handler += onEvent2; // same as handler.add(onEvent2);	msg += " (revisited!)"; // add new data	handler(msg); // same as handler.changed(msg);}
Now just compile it and have fun!!! :lol:

One thing I want to comment about functors and function pointers is that they are interchangeable. If you define a class with a functor, you can use it as if it were a function pointer! See functor docs for more information.

Any questions just post, I'll try to monitor this tutorial.

Share this post


Link to post
Share on other sites

Sorry, I wrote the class declaration wrong. The data members should be "protected:" and not defaulted to private, so that it will be subclass-able.

Change the top of the "class Event" declaration as follows:

template<class Data> class Event {	protected: // protected so that subclasses can inherit the data	typedef void (*fptr)(Data);	std::vector<fptr> listeners;	// the rest of code...

Sorry!

Share this post


Link to post
Share on other sites

2 - how can you change the event for a diferent objects?

Are you joking ? Did you read the words

skeleton code into event.h:

Concerning the "how you can do it inside a class" part, I would simply say "you should not do it inside the class", or "your teacher should make you unable to do it inside the class" :D

Share this post


Link to post
Share on other sites

Are you joking ? Did you read the words

Concerning the "how you can do it inside a class" part, I would simply say "you should not do it inside the class", or "your teacher should make you unable to do it inside the class" :D

thanks for answer me. thanks for all

Share this post


Link to post
Share on other sites

why now, i only can see these text:

"Hey everyone, I'm going to teach you how to make a simple event handler in C++.

If you happen to be familiar with C#'s event handler system, this is going to mimic that.

I got the idea from a tutorial on how to make this same class in Python, but I couldn't find anything comparable to it in..."

 

i can't see the vertical scroolbar :(

why these happens?

Share this post


Link to post
Share on other sites

why now, i only can see these text:

"Hey everyone, I'm going to teach you how to make a simple event handler in C++.

 

If you happen to be familiar with C#'s event handler system, this is going to mimic that.

 

I got the idea from a tutorial on how to make this same class in Python, but I couldn't find anything comparable to it in..."

 

i can't see the vertical scroolbar :(

why these happens?

Ouch! Problem with your browser? Which browser do you use? Works rather well with my Firefox display.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now