473,385 Members | 2,269 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Simple Event Framework - Suggestions?

Hi,
I'm looking for some ideas on how to build a very simple Event processing
framework in my C++ app. Here is a quick background ...

I'm building a multithreaded app in C++ (on Linux) that
uses message queues to pass pointers to Events between threads. In my app
there are simple events that can be defined using an enum (for example an
event called NETWORK_TIMEOUT) and more complex events that contain data
(for example an event called ALARM that contains a timestamp).

I've hacked together a quick solution that uses an Event class that
contains an enum identifying the type of event (ie. NETWORK_TIMEOUT,
ALARM, etc). For the more complex events that contain extra data a
subclass is created (ie. AlarmEvent) that contains the data. This
requires the thread that is receiving the event to check the enum and downcast
if necessary.

void processEvent(Event *event)
{
if(event->getEventType() == ALARM)
{
AlarmEvent *alarmEvent = static_cast<AlarmEvent *>event;
// process AlarmEvent here...
}
}

Does anyone have any suggestions on a better way to do this? The
downcasting seems a bit messy so maybe someone can suggest a more elegant
and typesafe way of doing this?

Thanks,
Mark
Jul 22 '05 #1
8 6063
Mark wrote:
Hi,
I'm looking for some ideas on how to build a very simple Event processing
framework in my C++ app. Here is a quick background ...

I'm building a multithreaded app in C++ (on Linux) that
uses message queues to pass pointers to Events between threads. In my app
there are simple events that can be defined using an enum (for example an
event called NETWORK_TIMEOUT) and more complex events that contain data
(for example an event called ALARM that contains a timestamp).

I've hacked together a quick solution that uses an Event class that
contains an enum identifying the type of event (ie. NETWORK_TIMEOUT,
ALARM, etc). For the more complex events that contain extra data a
subclass is created (ie. AlarmEvent) that contains the data. This
requires the thread that is receiving the event to check the enum and downcast
if necessary.

void processEvent(Event *event)
{
if(event->getEventType() == ALARM)
{
AlarmEvent *alarmEvent = static_cast<AlarmEvent *>event;
// process AlarmEvent here...
}
}

Does anyone have any suggestions on a better way to do this? The
downcasting seems a bit messy so maybe someone can suggest a more elegant
and typesafe way of doing this?

Thanks,
Mark


C++ has great built-in support for working with types, so work-arounds
like the "switch-on-type" approach you've described have become very
unfashionable. A better approach is to define a different class to
represent each type of Event, and overload the processEvent function for
each different type:

namespace Events
{
class Alarm { /* Data relevant to Alarm event. */ };
class NetworkTimeout { /* No extra data needed */ };
// Other event types...

void process( Alarm const& ) { /* ... */ }
void process( NetworkTimeout const& ) { /* ... */ }
// Functions to proces other event types...

template< typename Event >
void process( Event const& ) { /* Process generic event. */ }
}

Does this meet your needs?

-Jeff

Jul 22 '05 #2
Hi Jeff,

I hadn't considered this method, but it looks quite simple. I'm not sure
how it will work with my multithreaded app though because Events are
written onto a message queue and processed in another thread. I'm using an
EventDispatcher class that allows threads to register for certain events,
ie. one thread registers for ALARM events and another for NETWORK_TIMEOUT
events. When an event is generated, the EventDispatcher will send the
event to the appropriate thread(s). My EventDispatcher class doesn't know
about any concrete Events, only the Event base class. This way if I add a
new type of Event down the road I don't have to modify the EventDispatcher.

Given this info will your technique still work? Unfortunately my
experience with templates has been limited to simple std::vector<int> type
stuff.

Thanks Jeff I appreciate it!

Mark

Jul 22 '05 #3
Mark wrote:
Hi Jeff,

I hadn't considered this method, but it looks quite simple. I'm not sure
how it will work with my multithreaded app though because Events are
written onto a message queue and processed in another thread. I'm using an
EventDispatcher class that allows threads to register for certain events,
ie. one thread registers for ALARM events and another for NETWORK_TIMEOUT
events. When an event is generated, the EventDispatcher will send the
event to the appropriate thread(s). My EventDispatcher class doesn't know
about any concrete Events, only the Event base class. This way if I add a
new type of Event down the road I don't have to modify the EventDispatcher.

Given this info will your technique still work? Unfortunately my
experience with templates has been limited to simple std::vector<int> type
stuff.

Thanks Jeff I appreciate it!

Mark


Thanks for explaining; no, the technique I described won't work in this
case. Would it be possible to make "process" a virtual method of the
Event base class? This is a common way of implementing the sort of
polymorphism you seem to need. E.g.:

struct Event
{
virtual void process( ) =0;
};

struct Alarm: Event
{
void process( ); // Overrides Event::process( ).
private:
// Data relevant to Alarm event.
};

struct NetworkTimeout: Event
{
void process( ); // Overrides Event::process( ).
};

// Other event types...

Jul 22 '05 #4
On Wed, 31 Dec 2003 19:17:50 -0500, Jeff Schwab wrote:
Mark wrote:
Hi Jeff,

I hadn't considered this method, but it looks quite simple. I'm not sure
how it will work with my multithreaded app though because Events are
written onto a message queue and processed in another thread. I'm using an
EventDispatcher class that allows threads to register for certain events,
ie. one thread registers for ALARM events and another for NETWORK_TIMEOUT
events. When an event is generated, the EventDispatcher will send the
event to the appropriate thread(s). My EventDispatcher class doesn't know
about any concrete Events, only the Event base class. This way if I add a
new type of Event down the road I don't have to modify the EventDispatcher.

Given this info will your technique still work? Unfortunately my
experience with templates has been limited to simple std::vector<int> type
stuff.

Thanks Jeff I appreciate it!

Mark


Thanks for explaining; no, the technique I described won't work in this
case. Would it be possible to make "process" a virtual method of the
Event base class? This is a common way of implementing the sort of
polymorphism you seem to need. E.g.:

struct Event
{
virtual void process( ) =0;
};

struct Alarm: Event
{
void process( ); // Overrides Event::process( ).
private:
// Data relevant to Alarm event.
};

struct NetworkTimeout: Event
{
void process( ); // Overrides Event::process( ).
};

// Other event types...


Looks like the Command pattern. This would be a nice solution, except
that the Event and the processing associated with the event can vary
independantly. For example I have one thread that is managing a socket
connection and another that is managing a log. Both register to receive
the ALARM event but the processing done is different in each case (ie.
send a message over the socket, write an entry into the log). Maybe I'm
going about the solution the wrong way. I considered having a separate
Command pattern type class that is passed into the Event when registered,
but that would require writing *alot* of small classes which doesn't
appeal to me. Maybe I'll come with something better tomorrow, I've been
at it a while today...

Thanks!
Mark

Jul 22 '05 #5
Mark wrote:
On Wed, 31 Dec 2003 19:17:50 -0500, Jeff Schwab wrote:

Mark wrote:
Hi Jeff,

I hadn't considered this method, but it looks quite simple. I'm not sure
how it will work with my multithreaded app though because Events are
written onto a message queue and processed in another thread. I'm using an
EventDispatcher class that allows threads to register for certain events,
ie. one thread registers for ALARM events and another for NETWORK_TIMEOUT
events. When an event is generated, the EventDispatcher will send the
event to the appropriate thread(s). My EventDispatcher class doesn't know
about any concrete Events, only the Event base class. This way if I add a
new type of Event down the road I don't have to modify the EventDispatcher.

Given this info will your technique still work? Unfortunately my
experience with templates has been limited to simple std::vector<int> type
stuff.

Thanks Jeff I appreciate it!

Mark


Thanks for explaining; no, the technique I described won't work in this
case. Would it be possible to make "process" a virtual method of the
Event base class? This is a common way of implementing the sort of
polymorphism you seem to need. E.g.:

struct Event
{
virtual void process( ) =0;
};

struct Alarm: Event
{
void process( ); // Overrides Event::process( ).
private:
// Data relevant to Alarm event.
};

struct NetworkTimeout: Event
{
void process( ); // Overrides Event::process( ).
};

// Other event types...

Looks like the Command pattern. This would be a nice solution, except
that the Event and the processing associated with the event can vary
independantly. For example I have one thread that is managing a socket
connection and another that is managing a log. Both register to receive
the ALARM event but the processing done is different in each case (ie.
send a message over the socket, write an entry into the log). Maybe I'm
going about the solution the wrong way. I considered having a separate
Command pattern type class that is passed into the Event when registered,
but that would require writing *alot* of small classes which doesn't
appeal to me. Maybe I'll come with something better tomorrow, I've been
at it a while today...


Aha! Sounds like you need call-backs: just one more level of
indirection.

For each type of thread, define a struct to provide handlers for all the
relevant types of Event. Provide a virtual method in the Event class;
inside the virtual method, the exact type of the Event can be known, and
the event can call back the appropriate routine from the Thread's
handler struct.

I've coded an approach here that assumes the Event module knows nothing
about what types of Thread may exist. The approach relies on two levels
of run-time indirection. If you're willing to couple the Events more
strongly to the Thread types, you can get away with only one level of
run-time indirection by overloading the Event::call method for each type
of Thread handler; that way, the Handler methods don't need to be
virtual, and in fact the base Handler class need not exist.

Please let me know if I've explained this poorly, or if there is any
problem with the code that makes this approach inappropriate.

-Jeff
namespace Events
{
struct Alarm;
struct Network_Timeout;

struct Handler
{
virtual void handle( Alarm& ) =0;
virtual void handle( Network_Timeout& ) =0;
};

struct Event
{
virtual void call( Handler& ) =0;
};

struct Alarm: Event
{
void call( Handler& h ) { h.handle( *this ); }
private:
// Alarm-specific data.
};

struct Network_Timeout: Event
{
void call( Handler& h ) { h.handle( *this ); }
};
}

#include <iostream>

namespace Threads
{
struct Socket_Connection
{
struct Handler: Events::Handler
{
void handle( Events::Alarm& )
{
std::cout <<
"A Socket_Connection thread is handling an "
"Alarm event.\n";
}

void handle( Events::Network_Timeout& )
{
std::cout <<
"A Socket_Connection thread is handling a "
"Network_Timeout event.\n";
}
};

void process( Events::Event& event )
{
Handler handler;

event.call( handler );
}
};

struct Log_Manager
{
struct Handler: Events::Handler
{
void handle( Events::Alarm& event )
{
std::cout <<
"A Log_Manager thread is handling an "
"Alarm event.\n";
}

void handle( Events::Network_Timeout& event )
{
std::cout <<
"A Log_Manager thread is handling a "
"Network_Timeout event.\n";
}
};

void process( Events::Event& event )
{
Handler handler;

event.call( handler );
}
};
}

int main( )
{
Threads::Socket_Connection socket_connection;
Threads::Log_Manager log_manager;

Events::Alarm alarm;
Events::Network_Timeout network_timeout;

Events::Event* events[ ] = { &alarm, &network_timeout };

for( Events::Event** p = events; p < events + 2; ++p )
{
socket_connection.process( **p );
log_manager.process( **p );
}
}

Jul 22 '05 #6
Mark wrote:
I'm looking for some ideas on how to build a very simple Event processing
framework in my C++ app.
There is no such thing as a simple event framework.

...

void processEvent(Event *event)
{
if(event->getEventType() == ALARM)
{
AlarmEvent *alarmEvent = static_cast<AlarmEvent *>event;
// process AlarmEvent here...
}
}
Urgh.

Does anyone have any suggestions on a better way to do this?


Move the downcasting from the client code to the event objects.

Generally that's called the visitor pattern.

It goes sort of like this:
struct EventHandlerBase {};

struct SomeSpecificEvent: GeneralEvent
{
struct HandlerInterface: EventHandlerBase
{
virtual void handle( SomeSpecificEvent const& ) = 0;
};

bool visit( EventHandlerBase& aHandler ) const
{
HandlerInterface* pHandler = dynamic_cast ...
if( pHandler != 0 ) pHandler->handle( *this );
return (pHandler != 0);
}
};
struct ClientObject: EventHandlerBase
{
...
e.visit( *this );
...
};


Jul 22 '05 #7
Mark wrote:
Looks like the Command pattern. This would be a nice solution, except
that the Event and the processing associated with the event can vary
independantly. For example I have one thread that is managing a socket
connection and another that is managing a log. Both register to receive
the ALARM event but the processing done is different in each case (ie.
send a message over the socket, write an entry into the log). Maybe I'm
going about the solution the wrong way. I considered having a separate
Command pattern type class that is passed into the Event when registered,
but that would require writing *alot* of small classes which doesn't
appeal to me. Maybe I'll come with something better tomorrow, I've been
at it a while today...


What you describe seems like the basic decoupling of the signals /
slots mechanism. This type of thing can be found in several libraries
(boost has a nice one) and can be really simple to drop into your own
app. If you must write it yourself, though, you should at least check
out a few implementations. There are a few basic patterns found in
them (several already discussed) and fixing everything up nicely can
be very instructive.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

galathaea: prankster, fablist, magician, liar
Jul 22 '05 #8
On Wed, 31 Dec 2003 22:03:41 -0500, Jeff Schwab wrote:

Aha! Sounds like you need call-backs: just one more level of
indirection.

For each type of thread, define a struct to provide handlers for all the
relevant types of Event. Provide a virtual method in the Event class;
inside the virtual method, the exact type of the Event can be known, and
the event can call back the appropriate routine from the Thread's
handler struct.

I've coded an approach here that assumes the Event module knows nothing
about what types of Thread may exist. The approach relies on two levels
of run-time indirection. If you're willing to couple the Events more
strongly to the Thread types, you can get away with only one level of
run-time indirection by overloading the Event::call method for each type
of Thread handler; that way, the Handler methods don't need to be
virtual, and in fact the base Handler class need not exist.

Please let me know if I've explained this poorly, or if there is any
problem with the code that makes this approach inappropriate.

-Jeff
namespace Events
{
struct Alarm;
struct Network_Timeout;

struct Handler
{
virtual void handle( Alarm& ) =0;
virtual void handle( Network_Timeout& ) =0;
};

struct Event
{
virtual void call( Handler& ) =0;
};

struct Alarm: Event
{
void call( Handler& h ) { h.handle( *this ); }
private:
// Alarm-specific data.
};

struct Network_Timeout: Event
{
void call( Handler& h ) { h.handle( *this ); }
};
}

#include <iostream>

namespace Threads
{
struct Socket_Connection
{
struct Handler: Events::Handler
{
void handle( Events::Alarm& )
{
std::cout <<
"A Socket_Connection thread is handling an "
"Alarm event.\n";
}

void handle( Events::Network_Timeout& )
{
std::cout <<
"A Socket_Connection thread is handling a "
"Network_Timeout event.\n";
}
};

void process( Events::Event& event )
{
Handler handler;

event.call( handler );
}
};

struct Log_Manager
{
struct Handler: Events::Handler
{
void handle( Events::Alarm& event )
{
std::cout <<
"A Log_Manager thread is handling an "
"Alarm event.\n";
}

void handle( Events::Network_Timeout& event )
{
std::cout <<
"A Log_Manager thread is handling a "
"Network_Timeout event.\n";
}
};

void process( Events::Event& event )
{
Handler handler;

event.call( handler );
}
};
}

int main( )
{
Threads::Socket_Connection socket_connection;
Threads::Log_Manager log_manager;

Events::Alarm alarm;
Events::Network_Timeout network_timeout;

Events::Event* events[ ] = { &alarm, &network_timeout };

for( Events::Event** p = events; p < events + 2; ++p )
{
socket_connection.process( **p );
log_manager.process( **p );
}
}


Hi Jeff,

Thanks again for posting this, it's always interesting to look at various
alternatives. I like this solution, the only possible disadvantage I see
is that adding a new Event requires modifying the Handler class with a new
handle() method. Maybe I'll try something like this out and see how it
works in my app. I could also accomplish the same thing with the Signals
and Slots library that the other poster mentioned, but neither the Boost
or the libsigc++ implementation is thread safe.

Thanks! I'll post again if I have any other ideas or questions about your
solution.

Mark

Jul 22 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Abhi | last post by:
Hi, I am looking to write a GPS emulator in C#. All the program does is outputs GPGLL (dont need to know any details, think if it as map coordinates) coordinates after a specific time interval...
2
by: veziak | last post by:
Dear colleagues, we have encountered some problems. there are some types of events in our system: business objects events, events that fire on time, on some conditions, and on user input. so we...
2
by: nataraj.gnanavadivel | last post by:
can any one please give me the UML relationship between the following event framework componenets: 1. EventSource 2. Event 3. EventListener 4. EventDispatcher/EventRouter The strategy may...
4
by: =?Utf-8?B?Y2FsZGVyYXJh?= | last post by:
Dear all, Until now I was programing for more that 6 years using VB.net simply because I come from that word and used to. Now I need to switch to C# simply because my customer want its project...
2
by: Bhasker V Kode | last post by:
Hi, I just wanted to share with everyone a new project that i'm launching today called the Returnable Project ( http://returnable.org ) Returnable serves as a open-platform architectural guide...
6
by: Arthur Dent | last post by:
Anyone know, in VB.NET (2005) how to prevent duplicate occurrences of the same event handler on an event? e.g... I have some object which raises an event, and some other class which consumes...
2
by: rynt | last post by:
Hello all. I'm Looking for suggestions for which Python web framework(e.g. Django, Turbogears,etc.) would work best to develop a cross platform browser based system to manage lab specimens. ...
3
by: Ravindran A | last post by:
Friends, I need to make a simple (windows) automation framework (a suite and couple of test cases) in Perl, that connects to a Nokia phone using USB interface and execute some tests. Please...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.