Sign in to follow this  

C++ Event Handling?

This topic is 3865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I did some quick research regarding C++ event-handling and all I found were huge chunks of code with little explanation. It would be nice if someone could explain to me how C++ event handling works? I'm also wondering; do extrernal APIs such as OpenGL have their own event-handling methodologies that are independent of C++'s? Please feel free to go in detail. Thank you.

Share this post


Link to post
Share on other sites
C++ doesn't have event handling. That's why external APIs all have their own, and it's also why you find tons of code when you search; You have to write your own event handling system from scratch, so there could be lots of code to do that.

Share this post


Link to post
Share on other sites
C++ has no event handling. (unless you consider exceptions events)

Most common aproach is to use callbacks - a function, that will get called, when something happens.


class MyCallback
{
void notify()
{
// do something
}
};

void perform_operation( MyCallback &cb )
{
// do something
// do some more
cb.notify(); // tell we're done
}




Obviously, this is rigid and useless. You'd need to hardcode the class, the parameters, the method....

Next, are templates

template < class Callback >
void perform_operation( Callback &cb )
{
// do something
// do some more
cb.notify();
}




Improvement, you can now use any class as callback, as long as it has a notify() method with no parameters.

Problem still remains, since you might want to change the method name, or use different methods, or not even know the method name during design...

Function pointers - rather than hardcoding a method, just store pointer to it.

template < class ObjectType, class P1 >
struct callback1
{
typedef ObjectType & reference;
typedef void (ObjectType::*MethodType)(P1);

callback1( reference instance, MethodType callback )
: m_instance( instance )
, m_callback( callback )
{}

void operator()( P1 p1 ) const {
(m_instance.*m_callback)(p1);
}

private:
reference m_instance;
MethodType m_callback;
};

class CallbackHandler
{
void notify( int x )
{
}
}

...
// Handler is our true callback
CallbackHandler handler;

// Wrap callback function and instance
callback1<CallbackHandler, int> int_callback( handler, &CallbackHandler::notify );

...

template < class Callback >
void perform_operation( Callback &cb )
{
// do something
// then notify the callback with some value, 15 for example
cb( 15 );
}

// call the function by specifying our callback
perform_operation( int_callback );





Lots of code....

callback1 is templated wrapper around an object. The whole purpose of that class is to store pointer to an instance of an object, and pointer to method within that object.

callback1 is a specialization of a callback for methods with 1 parameter. For more parameters, you'd have different classes.

Next, callback1 overloads the operator() with 1 parameter to allow the callback to be invoked semantically in the same way as a function.

CallbackHandler is some class, could be any class, which contains callback methods.

int_callback is an instance of a callback that maps between the actual implementation and generic callback handler.


Lastly, after getting a headache from all of this, look into boost::signal, libsigc++, or some other similar library, which does all of the above in a type-safe and thread-safe manner. Also important is that much of the above voodoo is hidden - usage is generally simple and straight-forward, much like declaring methods.

This aproach is somewhat ugly, but will result is extremly efficient code (frequently with no overhead), and retain full type-safety.

Also: there are alternatives to handling events. This is the generic aproach. You can always specialize them without templates, without variable parameters, and similar.

Share this post


Link to post
Share on other sites
Ok, there are some things I don't understand in the code:

Quote:

template < class ObjectType, class P1 >
struct callback1
{
typedef ObjectType & reference;
typedef void (ObjectType::*MethodType)(P1);


I don't understand what the '&' does in "typedef ObjectType & reference;"
And I have no idea what this is about: "typedef void (ObjectType::*MethodType)(P1);"

could you please explain?

Quote:

callback1( reference instance, MethodType callback )
: m_instance( instance )
, m_callback( callback )
{}


^ I'm not sure what that is, it looks like a constructor, but I don't understand the ": m_instance( instance ), m_callback( callback )" part, it looks like it's extending a superclass, I'm confused.

Quote:

void operator()( P1 p1 ) const {
(m_instance.*m_callback)(p1);
}


Ok, that's really confusing... Why are there two '()' and why is the 'const' just before the '{'?

Ok, Thanks.

Share this post


Link to post
Share on other sites
You need to look up information on function pointers. That will explain away alot of your confusion.

Your second question is in refrence to an initalizer list, these are fast ways to initalize your variables.

class foo
{
int a;
public:
foo() : a(5) { cout << a << endl; } //prints 5
}
//is faster than if you had used
foo() { a = 5; }
//and is the only way to do this
class bar
{
int &a;
public:
bar( int &refrenced_var ) : a(refrenced_var) {}
}
//since refrences have to point to something, you need the initalizer list
//to give 'a' a value.


in your third question, the "const" is to tell the compiler that the function has no sideeffect on the current class.
all the () are from the fact that Antheus is using function pointers. And, the
void operator()( P1 p1 )
part has () because it is 'operator()' with '(P1 p1)' as parameters.

Share this post


Link to post
Share on other sites
As Antheus said, the easiest way is to use Boost.Signals, Boost.Bind and Boost.Function to do all of this dirty work for you:


class Adder
{
int i;
public:
Adder(int i) : i(i) {}
int add(int a, int b) { return a + b + i; }
};

int main()
{
Adder adder(9);
boost::function< int (int, int) > f = boost::bind(&adder, &Adder::add, _1, _2);

// prints 21
std::cout << f(5, 7) << std::endl;

}


Note: It's been a long time since I wrote event code, so I may have made a mistake somewhere up there. It's more than likely.

Share this post


Link to post
Share on other sites

This topic is 3865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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

Sign in to follow this