Low latency callback in C++ - c++

I have an event driven application. I want to keep the event handler (EventHandler class capable of many/all events) a common implementation - while allowing the EventSource be changeable (specifically - at compile time).
To couple the EventHandler with the EventSource, I will have to store an instance of handler within the EventSource. I tried to store handlers of various forms:
pointer to an interface of EventHandler (that has public handler methods defined in concrete EventHandler's
instance of std::function - this provided greatest flexibility
However, in both cases, the latency in calling the target method/lambda was quite high (on my test setup about 250ns) - and to worse, was inconsistent. May be due to virtual table and/or heap allocation and/or type erasure ???
In order to reduce this latency, I want to make use of templates.
The best I could come up with is:
template <typename EventHandler>
class EventSource1
{
EventHandler* mHandler;
public:
typedef EventHandler EventHandlerType;
void AssignHandler (EventHandler* handler)
{
this->mHandler = handler;
}
void EventuallyDoCallback (int arbArg)
{
this->mHandler->CallbackFunction (arbArg);
}
};
template <EventSourceType>
class EventSourceTraits
{
typedef EventSourceType::EventHandlerType EventHandlerType;
static void AssignHandler (EventSourceType& source, EventHandlerType* handler)
{
source.AssignHandler(handler);
}
};
class EventHandler
{
public:
void CallbackFunction (int arg)
{
std::cout << "My callback called\n";
}
};
int main ()
{
EventSource1<EventHandler> source; /// as one can notice, EventSource's need not to know the event handler objects.
EventHandler handler;
EventSourceTraits<EventSource1>::AssignHandler (source, &handler);
}
This method impose a restriction that all my EventSource's to be a template classes.
Question is: Is this best way to achieve consistent and low latency to callback? Can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?

Is this best way to achieve consistent and low latency to callback?
As suggested in the comments to the question, I'd rather try and measure to know if that's really a problem and what's the best alternative for you.
There doesn't exist the best way, it mostly depends on the actual problem.
can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?
Maybe the following can be a good point from which to start to achieve that:
#include <iostream>
class EventSource1
{
using invoke_t = void(*)(void *C, int value);
template<typename T, void(T::*M)(int)>
static void proto(void *C, int value) {
(static_cast<T*>(C)->*M)(value);
}
invoke_t invoke;
void *handler;
public:
template<typename T, void(T::*M)(int) = &T::CallbackFunction>
void AssignHandler (T* ref)
{
invoke = &proto<T, M>;
handler = ref;
}
void EventuallyDoCallback (int arg)
{
invoke(handler, arg);
}
};
class EventHandler
{
public:
void CallbackFunction (int arg)
{
std::cout << "My callback called: " << arg << std::endl;
}
};
int main ()
{
EventSource1 source;
EventHandler handler;
source.AssignHandler(&handler);
source.EventuallyDoCallback(42);
}
See it on wandbox.

Related

What is an alternative to using templates on a virtual member function?

I am creating a simple event system where multiple listeners can be notified on a specific topic and when an event is fired, it can pass a generic payload to the event, and the listeners will match the format of the fired event. However, because it's not possible to use templates on a virtual function, how else can I achieve this?
class AEventListener
{
public:
template<class T>
struct PayloadObject {
T obj;
};
explicit AEventListener();
virtual ~AEventListener();
//error here because T is undefined. Each PayloadObject may have a different type
virtual void notify(vector<shared_ptr<PayloadObject<T>>> payload) = 0;
};
The notify method is called when an event topic has a listener subscribed, but I want a generic way of just passing a load of random objects to the listener.
For example
fireEvent("test.topic", Payload { 0, "hello", 123 });
//...
listener.notify(payload);
How would I go about this in C++?
I have managed to get around this, although I don't think this is the best way and could slow down performance.
template<class T>
struct PayloadObject : public APayloadObject {
T obj;
PayloadObject(T obj) {
this->obj = obj;
}
~PayloadObject() override {
};
};
struct APayloadObject {
virtual ~APayloadObject();
};
Firing:
vector<shared_ptr<APayloadObject>> payload;
payload.push_back(shared_ptr<PayloadObject<int>>(new PayloadObject<int>(5))); //payload[0] = int - 5
Events::fire(EventKeys::DISCONNECTION_EVENT, payload);
Notifying:
shared_ptr<PayloadObject<int>> number = dynamic_pointer_cast<PayloadObject<int>>(payload[0]);
int id = number.get()->obj; //payload[0] = int - 5
One simple approach is to come up with a common base or common interface for the Payload objects. So that they are not a template class.
struct Payload {
virtual ~Payload() = default;
virtual std::string foo() const;
virtual std::string bar() const;
};
Another way is to use a variant type for the payload objects:
using Message_t = boost::variant<A, B, C>;
and then make AEventListener take the Message_t type so that it doesn't require the member function to be a template.
class AEventListener
{
public:
virtual ~AEventListener();
virtual void notify(std::vector<Message_t> payload) = 0;
};
In C++17 you could use std::variant for this instead of boost.
Yet another way is to skip using a variant, and just make it so that the Listener must implement three different functions, one for each type:
class AEventListener
{
public:
virtual ~AEventListener();
virtual void notifyA(A payload) = 0;
virtual void notifyB(B payload) = 0;
virtual void notifyC(C payload) = 0;
};
More generally, it is pretty difficult in C++ to make a concept like "Function object that is callable with any particular type of arguments". This is in part because... it is not very useful, there is not much that you can do generically with data of ANY type that you can assume nothing about.
So I would suggest that you think hard about refining your Event Listener concept, and make more concrete what it is that objects of this type are ACTUALLY supposed to be required to do.

C++ polymorphism: what am I missing?

I am learning c++ and would like to build something similar to C# events to handle interrupts in an embedded c++ project.
So far I came up with a solution that does almost what I want. However I need some help with polymorphism (?). The following code snippet is kind of a minimum example to reproduce my situation:
#include <iostream>
struct Event
{ };
struct EventHandler
{
virtual void Esr (const Event& I) { }
};
struct EventSender
{
EventSender (EventHandler& Handler) : _Handler (Handler) { }
template <typename T>
void SendEvent (const T&) const
{
_Handler.Esr (T ());
}
EventHandler& _Handler;
};
struct SpecialEvent : public Event
{ };
struct MyHandler : public EventHandler
{
void Esr (const Event& I) override { std::cout << "Event" << std::endl; }
void Esr (const SpecialEvent& I) { std::cout << "SpecialEvent" << std::endl; }
};
int main()
{
MyHandler handler;
EventSender sender (handler);
/* Invoke directly */
handler.Esr (Event ());
handler.Esr (SpecialEvent ());
/* Invoke indirectly */
sender.SendEvent (Event ());
sender.SendEvent (SpecialEvent ()); // Expected cout msg: "SpecialEvent"
return 0;
}
Expected console output:
Event
SpecialEvent
Event
SpecialEvent
Actual console output:
Event
SpecialEvent
Event
Event
What does the compiler/linker here that I am not aware of?
Here you're trying to use overloading, not classic (virtual function based) polymorphism.
What you want (at least as I understand it) is behavior that's essentially the same between using a handler directly, and invoking it indirectly via a sender. The variation that happens is between an Event and a SpecialEvent.
That being the case, classic polymorphism would involve a virtual function in Event that's overridden in SpecialEvent:
struct Event {
virtual void operator()() const { std::cout << "Event\n"; }
};
struct SpecialEvent : public Event {
virtual void operator()() const override { std::cout << "Special Event\n"; }
};
With this in place, a reference (or pointer) to an Event will invoke the member for the actual type. Doing the polymorphism here means we only need one handler class, so the code ends up something like this:
#include <iostream>
struct Event {
virtual void operator()() const { std::cout << "Event\n"; }
};
struct EventHandler {
void Esr(const Event& I) const { I(); }
};
struct EventSender {
template <typename T>
void SendEvent (const T& t) const {
handler.Esr(t);
}
EventHandler handler;
};
struct SpecialEvent : public Event {
virtual void operator()() const override { std::cout << "Special Event\n"; }
};
int main() {
EventHandler handler;
EventSender sender;
/* Invoke directly */
handler.Esr (Event ());
handler.Esr (SpecialEvent ());
/* Invoke indirectly */
sender.SendEvent (Event ());
sender.SendEvent (SpecialEvent ()); // Expected cout msg: "SpecialEvent"
}
You have two methods in MyHandler. One of them overrides the base class method
The other one does not.
One solution would be to declare both methods in the base class:
struct EventHandler
{
virtual void Esr (const Event& I) = 0;
virtual void Esr (const SpecialEvent& I) = 0;
};
That way the compiler can use the type of the argument to resolve the method at the EventHandler level.
If you wanted to avoid the requirement that all derived classes must overload both methods you could do something like this:
struct EventHandler
{
virtual void Esr (const Event& I) = 0;
virtual void Esr (const SpecialEvent& I)
{
// if not overridden, use the non-specialized event handler.
Esr(reinterpret_cast<const Event &>(I));
}
};
To answer your question:
What does the compiler/linker here that I am not aware of?
In C++ a method call is resolved at compile/link time into either 1) a call to a particular block of code (the method body), or 2) an indirect call via a hidden data structure called a vtable. The actual vtable is determined at runtime, but the compiler has to decide which entry in the table to use for the call. (Google vtable for lots more information about what they are and how they are implemented.)
It has to base this resolution on what it's allowed to know. In this case based on the type of the pointer or reference through which the method is called. Note this is NOT necessarily the type of the actual object.
In your case when you call throgh handler the compiler is allowed to know about both methods declared in MyHandler so it can pick the one you expect, but when the call goes through sender, it has to find a method declared in EventSender. There's only one method declared in EventSender. Fortunately the argument can be coerced into a const Event & so the compiler is able to use that method. Thus it uses the vtable entry for that method. So it finds the vtable for MyHandler [at runtime] and uses the vtable entry for
Esr (const Event& I)
and that's how you end up in the wrong method.
BTW: My answer is intended to explain what you are seeing and give you a way to fix your immediate problem. Jerry Coffin's answer gives you an alternative approach that should work better for you in the long term.
First of all, you cannot cast references to a descendant of a base class.
You'll need to use a pointer to that type, and using dynamic_cast.
So, you have
EventSender sender (handler);
in main(). The constructor of sender binds to the base class of MyHandler which is EventHandler, since this is the parameter type in the constructor of MyHandler (= EventHandler::EventHandler). Therefore, EventHandler.Esr(const Event &) is called, which happens to be virtual, so there is a pointer to MyHandler.Esr(const Event &).
Note that technically, Esr(const Event &) and Esr(const SpecialEvent &) are two different methods; they just happen to use the same name.

Generic observer pattern in C++

In many cases in my application i need class A to register itself as a listener on class B to receive notification when something happens. In every case i define a separate interface B implements and A can call do. So for example, A will have the following method:
void registerSomeEventListener(SomeEventListener l);
Also, in many cases, B will need to support multiple listeners so i reimplement the registration and notifyAll logic.
One generic way i know is to have some EventListener (implement by A) and EventNotifier (implement by B) classes. In this case each event is identified by a string and A implements the method:
void eventNotified(string eventType);
I think this is not a good solution. It will result in many if-else statements in case A listens to several events and might result in bugs when event names are changed only in the listener or the notifier.
I wonder what is the correct way to implement the observer pattern in C++?
Take a look at boost::signals2. It provides a generic mechanism to define "signals" where other objects can register. The signal owner can then notify observers by "firing" the signal. Instead of register-methods, the subject defines signals as members which then keep track of connected observers and notify them when initiated. The signals are statically typed and accept every function with the matching signature. This has the advantage that there is no need for inheritance and thus a weaker coupling than the traditional observer inheritance hierarchy.
class Subject {
public:
void setData(int x) {
data_ = x;
dataChanged(x);
}
boost::signals2<void (int)> dataChanged;
private:
int data_;
};
class Observer {
public:
Observer(Subject& s) {
c_ = s.dataChanged.connect([&](int x) {this->processData(x);});
}
~Observer() {
c_.disconnect();
}
private:
void processData(int x) {
std::cout << "Updated: " << x << std::endl;
}
boost::signals2::connection c_;
};
int main() {
Subject s;
Observer o1(s);
Observer o2(s);
s.setData(42);
return 0;
}
In this example, the subject holds some int data and notifies all registered observers when the data is changed.
Lets say you have a generic event fireing object:
class base_invoke {
public:
virtual ~base_invoke () {};
virtual void Invoke() = 0;
}
But you want to fire events on different types of objects, so you derive from base:
template<class C>
class methodWrapper : public base_invoke {
public:
typedef void (C::*pfMethodWrapperArgs0)();
C * mInstance;
pfMethodWrapperArgs0 mMethod;
public:
methodWrapper(C * instance, pfMethodWrapperArgs0 meth)
: mInstance(instance)
{
mMethod = meth;
}
virtual void Invoke () {
(mInstance->*mMethod)();
}
}
Now if you create a wrapper for a collection of pointers to base_invoke you can call each fireing object and signal whichever method on whichever class you'd like.
You can also turn this collection class into a factory for the fireing objects. to simplyfy the work.
class Event {
protected:
Collection<base_invoke *> mObservers;
public:
// class method observers
template<class C>
void Add (C * classInstance, typename methodWrapper<C>::pfMethodWrapperArgs0 meth) {
methodWrapper<C> * mw = NEW(methodWrapper<C>)(classInstance, meth);
mObservers.Add(ObserverEntry(key, mw));
}
void Invoke () {
int count = mObservers.Count();
for (int i = 0; i < count; ++i) {
mObservers[i]->Invoke();
}
}
};
And your done with the hard work. Add an Event object anyplace you want listeners to subscribe. You'll probably want to expand this to allow removal of listeners, and perhaps to take a few function parameters but the core is pretty much the same.

C++ design help for templated virtual function

I'm trying to implement a number of classes based on a a common class that abstracts a thread-pool using boost.threadpool. I've got something that works (in Xcode on osx 10.7.2) but I'm really not sure its good design or if its even safe (largely because of what I've read on-line about the use of virtual member functions with templates). I'm looking for some style advice on the best way to implement something like this. I'm learning as I go along here so I know a lot of this will be 'bad form'...
I have a base class called 'workqueue' like this:
template <typename T>
class Workqueue{
private:
pool *pThreadPool;
public:
Workqueue (int);
void Start (T);
void Schedule (T);
virtual bool Process(T) {return true;}
};
template <typename T> Workqueue<T>::Workqueue(int thread_count){
pThreadPool = new pool(thread_count);
}
template <typename T> void Workqueue<T>::Start(T data){
pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));
pThreadPool->wait();
}
template <typename T> void Workqueue<T>::Schedule(T data){
pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));
}
I then define a new service based on this class like this:
struct Service1Data{
string item_data;
};
class MyService : public Workqueue<Service1Data> {
public:
MyService (int);
bool Process (Service1Data);
};
MyService::MyService(int workers) : Workqueue<Service1Data>(workers) {}
bool MyService::Process(Service1Data service_data){
cout << "in process (" << service_data.item_data << ")" << endl;
return true;
}
(I've removed as much of the code to keep it simple so as shown would run forever as it continually submits new work). I use the service like this:
MyService *service1 = new MyService(5);
Service1Data x;
x.item_data = "testing";
service1->Start(x);
// will wait until no more work.
delete service1;
so my specific questions:
firstly (and please be gentle...) is this bad form and is there a much better way to do this? (and why?)
secondly - is this even safe given the virtual/template issues? I read somewhere that it should be safe if the class itself is templated and I think I understand the basic vtable issues - but really not sure of the specifics.
thirdly - the base workqueue class needs to have the member definitions in the 'h' file with the class definition for it to link. Not sure why that would be - I imagine it's a linker issue to do with the virtual/template issues and so makes me nervous.
all help gratefully received..
Chris
I think, you shouldn't mix processing of data with processing of queue.
I mean, you shouldn't have Process method in your Workqueue. Data may process itself, or your queue can get processing function as (template?) parameter.
Then you get rid of all your problems with virtual function. YourService class then should agregate Workqueue and may provide process function.
Also, I doubt if you really need Workqueue. You can just use pThreadPool in YourService.
If you need a common interface for services, you should specify it explicitly & separately. Your inheritance chain looks unclear. inheritance means is. Why YourService is Workqueue. I do not believe! I think YourService can use any sort of queue. But usage is aggregation.
EDIT:
Code will look like this:
template<typename Processor>
class WorkQueue
{
public:
WorkQueue(int count, Processor& processor):_processor(processor),_pool(count) {}
template <typename Data>
void schedule(const Data& data)
{
_pool->schedule(std::bind(&Processor::process,_processor, data));
}
template <typename Data>
void run(const Data& data)
{
schedule(data);
_pool->wait();
}
private:
Processor& _processor;
pool _pool;
};
class Service
{
public:
virtual void run() = 0;
virtual ~Service() {}
};
struct ServiceParams
{
int param;
};
class MyService: public Service
{
friend class WorkQueue<MyService>;
public:
MyService(const ServiceParams& params): _params(params), _queue(1, *this) {}
void run() { return _queue.run(_params); }
private:
ServiceParams _params;
WorkQueue<MyService> _queue;
void process(const ServiceParams& params) {std::cout <<"hello, world\n";}
};
EDIT: I originally considered usage as:
ServiceData data;
Service* service = new MyService(data);
service->run();
delete service;
Little things I can obviously point out:
overuse of new and delete when you could create automatic objects.
For example, if your work-queue and the pool have the same lifetime then:
template <typename T> class Workqueue
{
private:
pool threadPool;
// // etc
};
template< typename T >
Workqueue::Workqueue( int numThreads ) : threadPool( numThreads )
{
}
Your base class needs a virtual destructor, and as it stands Start could call Schedule rather than implement the same line of code (with the boost::bind) twice. Ideally the constructor that takes an int member only will be declared explicit.
You probably need logic to wait for threads to complete.
I think a good design should isolate the queuing and work/task separately. In your design, both are tightly coupled. This design is good if you want to create separate pool for every type of work/task.
Another approach is to create a separate Work class containing the process function. Then your MyService will extend Work. And WorkQueue class will accept Work and by that means any derived class too. This approach is more generic in nature. So same worker queue can accept different type of work/task. Below code illustration will clear more.
Just to add this approach can also be used if you want to have different pool for different type of data. It is more flexible in nature.
template <typename T>
class Work{
T data; // contains the actual data to work on
public:
Work(T data) : data(data) {} // constructor to init data
virtual bool Process(T) {return false;} // returns false to tell process failed
T getData() { return data; } // get the data
};
class MyWork : public Work<Service1Data> {
public:
MyService (Service1Data data) :
Work(data) {}
bool Process (Service1Data); // Implement your work specific process func
};
bool MyWork::Process(Service1Data service_data){
cout << "in process (" << service_data.item_data << ")" << endl;
return true;
}
class Workqueue{
private:
pool *pThreadPool;
public:
Workqueue (int);
void Start (Work);
void Schedule (Work);
};
Workqueue::Workqueue(int thread_count){
pThreadPool = new pool(thread_count);
}
void Workqueue::Start(Work workToDo){
pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));
pThreadPool->wait();
}
void Workqueue::Schedule(Work data){
pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));
}
Usage
Service1Data x;
x.item_data = "testing";
MyWork myWork(x);
Workqueue wq = new Workqueue(5);
wq->Start(myWork);
// will wait until no more work.
delete service1;
Now to achieve different pools for different type of work/task, create two Workqueue with different pool size and then give one only one type of work and other another type of work.
NOTE: Above code might contain syntax errors, it just there to convey the design. Treat it as pseudo code.

Event Callback Daemon

I am working on an event daemon in C++ that I would like to use member function callbacks. Basically an event queue would collect events which the daemon continuously services. There is a base class Event struct with an ID and all events would derive from it. I would like the methods registered for each event to use the derived event type in their signature.
struct Event
{
unsigned int eventId;
};
struct EventA : public Event
{
unsigned int x;
unsigned int y;
};
// and struct EventB, EventC (use your imagination...)
const unsigned int EVENT_A = 1;
const unsigned int EVENT_B = 2;
const unsigned int EVENT_C = 3;
class Foo
{
public:
void handlerMethod_A(const EventA& e);
void handlerMethod_B(const EventB& e);
};
class Bar
{
public:
void handlerMethod_C(const EventC& e);
};
Then the Daemon would allow these classes to subscribe their member functions using their 'this' pointer.
class EventDaemon
{
public:
void serviceEvents();
template <class CallbackClass, class EventType>
void subscribe(
const unsigned int eventId,
CallbackClass* classInstancePtr,
void (CallbackClass::*funcPtr)(EventType));
private:
Queue<Event*> eventQueue_;
};
So outside this class you could do something like:
EventDaemon* ed = new EventDaemon();
Foo* foo = new Foo();
Bar* bar = new Bar();
ed->subscribe(EVENT_A, foo, Foo::handlerMethod_A);
ed->subscribe(EVENT_B, foo, Foo::handlerMethod_B);
ed->subscribe(EVENT_C, bar, Bar::handlerMethod_C);
And the EventDaemon loop would be along the lines of
void EventDaemon::serviceEvents()
{
while (true)
{
if (eventQueue_.empty())
{
// yield to other threads
}
else
{
// pop an event out of the FIFO queue
Event e* = eventQueue_.pop();
// somehow look up the callback info and use it
classInstancePtr->*funcPtr(reinterpret_cast<?*>(e));
}
}
}
So my question is how I can store the 'this' pointers and member function pointers in some sort of array by event ID. That way I could look up the 'classInstancePtr' and 'funcPtr' by using e->eventId and the event type as well for the reinterpret cast.
You are working too hard. Use boost functions:
http://www.boost.org/doc/libs/1_47_0/doc/html/function.html
These work whether you have a object or not. They will increase your compile time.
Note, whenever you come across these types of questions where you know many people must have had the same problem, there is probably a simple option and, if it is not in the standard library, it is probably in boost.
In response to Nick, I'm constantly throwing boost function objects into vectors and whatnot.
I've found that, while boost function objects can hold object references, having them do so can lead to bugs with object lifetimes and it is better to have them hold copies of the class objects (you run into the same bugs however you try to hold a reference to a object instance that you don't necessarily control the lifetime of). The pattern:
class Foo
{
struct Member
{
// member variable definitions
};
shared_ptr<Member> m_; // the only real member variable
public:
// etc. including the all-important copy
// constructor and assignment operator and
// don't forget the member function that gets stuck into
// the boost function as a callback!
};
where all the member variables get held in a shared_ptr allows for good performance and you don't have to worry about lifetimes of objects held by function objects because you can copy them by value. Threaded code (what I always seem to be writing nowadays) needs additional things like at least one boost mutex element in Member or some other way to assure values don't get stomped on.
boost::function [or, if your system supports it, std::function] will take care of holding the this pointer quite well, with the added benefit of not requiring an actual object if it isn't necessary. So instead of void (SomeType::*)(EventA) you have std::function<void(EventA)>, and you call std::bind as appropriate.
subscribe(EVENT_A, std::bind(&foo::handleEventA, &foo, std::placeholders::_1));
A trivial wrapper function can be used to provide the same signature as you originally proposed and hide the nasty placeholders.
You do, of course, still have the issue of each event type having its own signature, and the need to ensure you use the correct Event ID code. In both cases, your base Event type can help out. Your callback need not accept an EventA&; it can accept an Event&, and dynamic_cast it to an EventA at runtime. For the ID, query the type directly.
struct Event {
virtual void ~Event() { }
virtual int ID() =0;
};
template<typename E>
struct EventHelper : Event {
virtual int ID() { return E::EventID; }
};
struct EventA : EventHelper<EventA> {
static const int EventID = 89;
};
Now, if you have an Event* object [when you go to dispatch your events], you can do p->ID() to get the appropriate ID, and if you have a EventA type [when you register your callbacks] you can do EventA::EventID.
So now, all you have to store is a std::function<void(const Event&)> and an associated int value for each of your callbacks, no matter what the actual type of event you have.
void subscribe(int id, std::function<void(const Event&)> f) {
callbacks.insert(std::make_pair(id, f));
}
template<typename E>
void subscribe(std::function<void(const Event&)> f) {
subscribe(E::EventID, f);
}
template<typename O, typename E>
void subscribe(O* p, void (O::*f)(const Event&)) {
subscribe<E>(std::bind(f, p, std::placeholders::_1));
}
You still have the issue that user error when subscribing can result in a function being called incorrectly. If you've used dynamic_cast correctly within the callback, this will get caught at runtime, but a compile time check would be nice. So what if we automate that dynamic_cast? For this step, I'm going to use c++11 lambdas, but it can be implemented in C++03 as well using a variety of methods.
template <class CallbackClass, class EventType>
void subscribe(CallbackClass* classInstancePtr, void (CallbackClass::*funcPtr)(EventType)) {
subscribe<EventType::EventID>([&](const Event& e) {
(classInstancePtr->*funcPtr)(dynamic_cast<const EventType&>(e));
});
}
So now we've gone full circle back to your original interface where your callbacks accept the actual type they are going to be working on, but internally you've squeezed them all into a common signature.
Okay, so I finished an implementation of my original desired interface. I was looking through Dennis' answer but eventually got lead to functors and I realized what I was looking for was a simple polymorphic solution. I failed to grasp before that I could create a non-templated base class with which to use for storing templated classes in vectors/arrays. I think this is what mheyman was trying to tell me... so I apologize I didn't get it right away. Just to clarify though I was really looking for the implementation solution for my own benefit and knowledge, not just a 3rd party library to get the job done. So I guess I would be looking for how Boost functions work, not just that they exist and are awesome.
If anyone is still interested here are the important parts of what I ended up with (minus some extraneous stuff and error checking):
EventFunctor is basically a pointer to member function template class
EventFunctorBase is the non-templated base class used to store them in a vector
The Event is dynamic cast using the templated type before being used to invoke the callback
class EventDaemon
{
public:
template <class CallbackClass, class EventType>
void subscribe(
const EventId eventId,
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&));
private:
EventFunctorBase* callbacks_[MAX_NUM_EVENTS];
};
template <class CallbackClass, class EventType>
void EventDaemon::subscribe(
const EventId eventId,
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&))
{
callbacks_[eventId] = new EventFunctor<CallbackClass,EventType>(callbackClassInstancePtr,funcPtr);
}
class EventFunctorBase
{
public:
EventFunctorBase();
virtual ~EventFunctorBase();
virtual void operator()(const Event& e)=0;
};
template <class CallbackClass, class EventType>
class EventFunctor : public EventFunctorBase
{
public:
EventFunctor(
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&));
virtual void operator()(const Event& e);
private:
CallbackClass* callbackClassInstancePtr_;
void (CallbackClass::*funcPtr_)(const EventType&);
};
template <class CallbackClass, class EventType>
EventFunctor<CallbackClass,EventType>::EventFunctor(
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&))
:
callbackClassInstancePtr_(callbackClassInstancePtr),
funcPtr_(funcPtr)
{
}
template <class CallbackClass, class EventType>
/*virtual*/ void EventFunctor<CallbackClass,EventType>::operator()(const Event& e)
{
(callbackClassInstancePtr_->*funcPtr_)(dynamic_cast<const EventType&>(e));
}
EventDaemon loop
while (true_)
{
if (eventQueue_->empty())
{
// yield to other threads
}
else
{
Event* e = eventQueue_.pop();
(*(callbacks_[e->ID]))(*e);
}
}
My final steps here will be to try and remove the need to have the developer define an ID for each event... of course this might end up a new post later this week.