Jump to content

  • Log In with Google      Sign In   
  • Create Account

We need your feedback on a survey! Each completed response supports our community and gives you a chance to win a $25 Amazon gift card!


Events with lambdas


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 Six222   Members   -  Reputation: 439

Like
0Likes
Like

Posted 04 January 2014 - 03:12 PM

I've been playing around and trying to get to grips with some of the new C++ features and decided to do a little project using them, I really like lambdas I think they are a really great addition. However I'm having a few issues on coming up with a way to use them in an event system.

 

Ideally I'm trying to design a system where components can register themselves to listen for certain events and when an event is triggered these registered components are notified. My first implementation of it worked well, here's what I had:

class EventManager
{
public:
	void AddListener(std::string name, std::function<void(void)> func)
	{
		events[name].push_back(func);
	}

	void TriggerEvent(std::string name)
	{
		//Error checking here
		for (auto it = events[name].begin(); it != events[name].end(); it++)
		{
			(*it)();
		}
	}
	
private:
	std::map<std::string, std::vector<std::function<void(void)>>> events;

};

int main()
{
	EventManager ev;

	auto func = []() {std::cout << "Hello world!" << std::endl; };
	ev.AddListener("hello", func);
	ev.AddListener("hello", func);

	ev.TriggerEvent("hello");
	system("PAUSE");
	return 0;
}

As I said this works quite well but it's fairly basic.. Now my problem that I'm trying to solve is I'd like to be able to pass around variables. For example on a "WindowResize" event I'd like to be able to pass the new width and height to each function but still be able to maintain them in one class.

 

My second attempt used polymorphism to remove the limit of only being able to store std::function<void(void)>'s which looks like this:

class IEvent
{
public:
	virtual void dummy() = 0;
};

template<class T>
class Event : public IEvent
{
public:
	Event(T t) : func(t) { }

	virtual void dummy()
	{

	}

	T func;
};

class EventManager
{
public:
	void AddListener(std::string name, IEvent* func)
	{
		events[name].push_back(func);
	}

        //This is incorrect.
	template<class... Args>
	void TriggerEvent(std::string name, Args... args)
	{
		//Error checking here

		for (auto it = events[name].begin(); it != events[name].end(); it++)
		{
			//Call function here
		}
	}
	
private:
	std::map<std::string, std::vector<IEvent*>> events;

};

int main()
{
	EventManager ev;

	Event<std::function<void(int, int)>> something([](int x, int y) {std::cout << "X: " << x << "Y:" << y << std::endl; });

	ev.AddListener("hello", &something);
	ev.AddListener("hello", &something);

	ev.TriggerEvent("hello");
	system("PAUSE");
	return 0;
}

But I'm unable to figure out how to actually trigger the event and pass in the correct parameters. When I got to this point I realized that I can't use the variadic template in that way and that also events with different function types could be stored in the same vector which causes horrible run time errors when called... If anyone could point me in the right direction on how to "properly" do this that'd be great smile.png

 

Thanks.


Edited by Six222, 04 January 2014 - 03:28 PM.


Sponsor:

#2 SillyCow   Members   -  Reputation: 899

Like
0Likes
Like

Posted 04 January 2014 - 03:38 PM

Does anyone outside of the event have to know of X and Y?

if not, use the the "capture" instead of passing the variables.

That way all of your events have the same signature.

int x = 123;
int y = 345;
auto something=[=x, =y]() {std::cout << "X: " << x << "Y:" << y << std::endl; };

This way whoever executes the event will all ways


My new android game : Enemies of the Crown

My previous android game : Killer Bees


#3 Six222   Members   -  Reputation: 439

Like
0Likes
Like

Posted 04 January 2014 - 03:43 PM

Does it work that way? Each component that registers passes in it's own function to be called.. Can it capture variables from the class that triggers the event?

For example

 

"RenderManager" registers itself to listen for event "WindowResize" and passes in function to be called when triggered.

 

"WindowManager" triggers "WindowResize" event with parameters w = 800, h = 600. 

 

Can the function that "RenderManager" passes in capture the variables from "WindowManager" ?



#4 SillyCow   Members   -  Reputation: 899

Like
0Likes
Like

Posted 04 January 2014 - 05:38 PM

No it does not.

I misunderstood the question.

 

Quick and dirty solution:

You could use a "union" as your parameter, it theoretically wastes memory.

But since the union will only be on the stack you can do whatever you want.

 

Nicer solution (not sure if this works):

If you want non static links, you could have event manager keeps and returns void*.

The invoker (WindowManager) knows what type of function it is calling, so it calls a template function with which accepts "void *" and casts it to the right "vector<function<>(...)>", and calls all events.


My new android game : Enemies of the Crown

My previous android game : Killer Bees


#5 Krohm   Crossbones+   -  Reputation: 3262

Like
0Likes
Like

Posted 06 January 2014 - 01:27 AM

In all event-driven designs I've seen - including Javascript, which does not even have an explicit notion of type - event callbacks get a well defined set of parameters.

In my events, when the parameter list is unclear, I include only the parameters more likely to be used and pass a reference to the event manager itself for manual extraction of the required values.

Capture lists cannot do that either: they capture in the "declaration" scope, not in execution.



#6 Jason Z   Crossbones+   -  Reputation: 5454

Like
1Likes
Like

Posted 06 January 2014 - 05:57 AM

If you think about it, any object that wants to be notified of an event has to already be aware of what that event is, right?  If so, then you can have an arbitrary std::function signature for each event type, and each handler that gets registered will be of the correct type (or else the registration of the handler wouldn't have the right signature).

 

To handle multiple event signatures, I would suggest that you either have your event manager allow event types to be registered with it - or you could even do away with event managers all together and just have objects registering with one another.  The former provides a nice single point of access for monitoring events, while the latter gives you significantly more flexibility to add new systems without a centralized event type ID system.



#7 larspensjo   Members   -  Reputation: 1561

Like
0Likes
Like

Posted 07 January 2014 - 04:15 PM

For an example of a C++ event manager that is type safe, simple to use and very efficient, see SimpleSignal.

 

As far as I have found, it provides the optimal solution for me.


Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/

#8 Servant of the Lord   Crossbones+   -  Reputation: 21217

Like
0Likes
Like

Posted 07 January 2014 - 04:29 PM

I'm used nano-signal-slot.

 

For an example of a C++ event manager that is type safe, simple to use and very efficient, see SimpleSignal.

 

As far as I have found, it provides the optimal solution for me.

 

Did you make that?


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#9 larspensjo   Members   -  Reputation: 1561

Like
1Likes
Like

Posted 08 January 2014 - 02:43 AM

I'm used nano-signal-slot.

 

For an example of a C++ event manager that is type safe, simple to use and very efficient, see SimpleSignal.

 

As far as I have found, it provides the optimal solution for me.

 

Did you make that?

 

It was created by Tim Janik, I just put a copy in Github to make it more accessible to my projects and others that would need it. It is not copyrighted by Tim, but there is a readme that clearly attributes the source to him.


Current project: Ephenation.
Sharing OpenGL experiences: http://ephenationopengl.blogspot.com/




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS