Sign in to follow this  

Getting rid of void*

This topic is 368 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

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 : )

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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. 

Edited by Felix Ungman

Share this post


Link to post
Share on other sites

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)

Edited by Angelic Ice

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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>.

Share this post


Link to post
Share on other sites

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?

Share this post


Link to post
Share on other sites

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.

Edited by Nanoha

Share this post


Link to post
Share on other sites

 

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? 

Share this post


Link to post
Share on other sites

Nanoha, is this inspired by anything? It sounds pretty interesting. I thought about studying signals in Boost, would that be worth?

 

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

Well, my Event_Listener<T> as inheritance to a listener allows me to have on_even-functions per template type that I inherit.

I can put them in a register-object and store those in my data-structure within the caller. Additionally, make them accessible via type index.

 

I'm not saying this is the best implementation, just something that felt like it would work on a decent level for my circumstance.

I have no idea how I would let my caller call my objects without using event_listener classes they can inherit from without a template-type.

 

But maybe I'm misunderstanding your question in general, sorry : /

Share this post


Link to post
Share on other sites

Nanoha, is this inspired by anything? It sounds pretty interesting. I thought about studying signals in Boost, would that be worth?

 

I don't recall if I saw it anywhere, I came across lambdas while reading a book (in the appendix of c++ Concurrency in Action I believe) and tried them for events. Apparently it is a fairly common method. I can't stress enough just how much easier and cleaner it is using this method. I've done event systems the way you are now in the past and they have been an absolute nightmare and they are messy. There's no templates or inheritance required.

 

I'd say the lambda/std::function method is cleaner, easier to follow and maintain and also more powerful and flexible. I wish I had a guide to share since my implementation is probably not the best.

Share this post


Link to post
Share on other sites

I went back into the mind-factory and tried to put all your pieces of advice into a new implementation idea.

So, if I want to get rid of all those template-types, can I make a listener do something along this?

std::function<void(void)> function = std::bind(&Window::on_close_event, this);

And pass that to my caller's add_listener-function?

 

I'm not sure how to pass it, probably by value, as the original one will go out of scope just after doing std::function.

 

@[member='Felix Ungman'], mentioned, if I omit the inheritance, I could do this:

 

 

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

 

I would like to avoid specific methods for a specific event-type at all. That is what I liked about my template-implementation. Yes, it had some ugly overhead in different areas.

 

That means, one void to add all possible listeners.

I cannot think of how to make it without passing a template type of event I want to assign it.

As the std::function<void(void)> is the function that shall be called once a fitting trigger has been called.

 

An idea:

void Add_Listener(std::function<void(void)> function, Event_Type event_type)
{

// Create a Registration-object and map this to the key event_type. 

}

So my listener just needs to know the event_type it wants to listen to, which is no problem, and send the function that shall be called.

To stop listening is already solved, I removed that part in order to keep my examples simple.

 

Per hash-key, I want to have a vector of Registrations. Later, when an event has been triggered, the caller will iterate over that fitting vector and call all std::functions with fitting variables.

But since I saved them as std::function<void(void)>, is that even ok or possible?

 

I feel like std::function<void(void)> might not be right way. My intention is, as stated above, to hide/generalise it in order to avoid specific adders, as the parameter-list will require different types.

Especially if I have a connector per possible event type, that will become very ugly code and possibly bloated code, hence I had my template-idea.

On the other hand, std::function<void(void)> seems to be not safe, if it is even possible to do it this way.

Edit: Wouldn't it better to use std::function passing around a specific event type (e.g. Click_Event), using Event_Type as its base? That would guarantee, that all std::functions use the same.

 

I would be very happy if somebody could me feedback on my idea.

 

Thanks a lot for taking your time : )

Edited by Angelic Ice

Share this post


Link to post
Share on other sites

Huh? I have an unordered map with keys to each event type.

So, every value of a key is a vector with std::functions to different Listeners, yes, but they all care about the same object - the same values.

Where is the problem with that though?

 

I will take a look at the link, thanks : )

Edited by Angelic Ice

Share this post


Link to post
Share on other sites

This topic is 368 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