Getting rid of void*

Started by
13 comments, last by Angelic Ice 7 years, 4 months ago

Hello forum!

I have a event-listener-system.

Example: A window tells the caller-system (who stores listeners and tells whether an event has been triggered) it wants to start listening.

If the caller receives the information of a triggered event, it will notify all listeners to that specific event.

Every listener derives from a handler-class for each event they want to listen to.


class Window_Changer : public Event_Handler<Click_Event>

Here is what a window-class calls to register itself:


click_event_listener(event_bus.Add_handler<Click_Event>(*this))

My caller statically casts the this-pointer to a void-pointer that can be stored.

Later, when the listened event has been triggered, I static cast the void-pointer to a Event_Handler<T> and later cast it to what T is.

I really want to get away from this C-style of using void-pointers though.

Assuming std::function would be a good direction, I'm still a bit rusty in terms of using it.

How could I turn my object into a std::function instead of a void-pointer?

To be more precise:


// call from the listener
click_event_listener(event_bus.Add_handler<Click_Event>(*this))

// happening in the Add_handler-method
static_cast<void*>(&object)

Are there certain things to be aware of?

Can I static_cast back from that to a generic Event_Handler<T>, as I did above?

Thanks for taking your time : )

Advertisement

First, there is nothing bad about using void* in "the right way" to me but you need to use somethign else. Why not let event handlers inherit from certain EventArgs class as it is the thing in C#?

Other solution would be a templated class that encapsulates inheritance like "object" does in C# and Java. There is a good tutorial of an object like Any class on CodeProject website https://www.codeproject.com/articles/11250/high-performance-dynamic-typing-in-c-using-a-repla

However, I would recomend to use an EventArgs base class so you prevent passed pointers that were not part of your event system

It sounds to me that you are deriving from a base class, why not cast to that instead of the void*. Added benefit is that you can just call the handle function on these classes, assuming its part of the interface.

class EventHandler
{
public:
    void HandleEvent() = 0;
};
 
template<class T>
class EventHandlerT : public EventHandler
{
};

THis would work fine and you can now store pointers to EventHandler instead of void* in a container. std::function and std::bind are options you should avoid in favour of lambdas if you can, lambdas are more efficient.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

The std::function is really nice, code would be something like this:


void Event_Bus::Add_click_handler(std::function<void(Click_Event)> handler) {
   // save the handler somewhere
}

With no need for special interface classes that you need to derive from, code becomes much cleaner.


event_bus.Add_click_handler([](Click_Event event) {
   // handle the event here
});

Note that you can capture variables between [ and ] if you need to.

openwar - the real-time tactical war-game platform

Thanks everyone : )

It sounds to me that you are deriving from a base class, why not cast to that instead of the void*. Added benefit is that you can just call the handle function on these classes, assuming its part of the interface.

But how would I cast my Listener with the Handler<Click_Event> base down to Handler<Event_Type>? Click_Event is the template type with the base class of an Event_Type.

Because my registrations would need to store a pointer to that instead of void*, right?

Boost seems to have a very neat way of handling this (I wish I would have known before). But it would be weird to rebuild that, as I could just use their library.

(just hoping my way of doing it won't be a performance killer)

As others have already stated, std::function is a good solution. But beware if you ever want to implement a remove listener method, because std::function cannot compared to each other. In that case you'll need to resort to some c-style trickery again.

But how would I cast my Listener with the Handler<Click_Event> base down to Handler<Event_Type>? Click_Event is the template type with the base class of an Event_Type.
Because my registrations would need to store a pointer to that instead of void*, right?


NightCreature was suggesting that you store references to a list of Handler (note lack of template argument), not Handler<Event_Type>.

NightCreature was suggesting that you store references to a list of Handler (note lack of template argument), not Handler.

Hm, but my Handler<Event_Type> is my handler. Otherwise, I do not understand what else to store.

As all my Listeners have an inheritances of those. Or should I store a reference to that exact handler?

I've become a huge fan of using lambdas and std::function for my events now.


typedef std::function<void(float, float)> ConfigChangedFunc;
// Method of something that can be listened to:
// All this does is store the std::function in a map
void RegisterForEventConfigChanged(void* reference, ConfigChangedFunc callback)
{
        m_ConfigChangedFuncMap[reference] = callback;
}

// In some object that wants to listen for events:
Window::GetInstance()->RegisterForEventConfigChanged(this,
	[this](float width, float height)
{
	OnConfigChanged(width, height);
});

I pass in the 'this' pointer just as a reference for when I want to unregister events, it is never used beyond this. I remember the bad old days of making event systems without std::function/lambdas and I would hate to go back. Objects don't need to inherit from a base listener class or anything like that; they are far more independent.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

NightCreature was suggesting that you store references to a list of Handler (note lack of template argument), not Handler.

Hm, but my Handler<Event_Type> is my handler. Otherwise, I do not understand what else to store.

As all my Listeners have an inheritances of those. Or should I store a reference to that exact handler?

What does the template buy you that a simple Handler class hierarchy would not?

Stephen M. Webb
Professional Free Software Developer

This topic is closed to new replies.

Advertisement