Delagates c++

Started by
3 comments, last by Krohm 11 years, 2 months ago

This question is just in references to what one of the posts that have been made. There was some code posted I just have a quick question, as I will be writing my own component + event management system. With delagates what is the fastest / best way to write them in c++. Please state why x is fastest etc.

Orignal quote...

ComposerCookie, on 14 Feb 2013 - 23:16, said:snapback.png



can you please explain a bit more detail?

So, you have an "event", which is just an id to distinguish between events. It can be an enum, #DEFINE'd ints, hashed strings, whatever.

You also have a "delegate", which is the code that should be executed when the event happens. Delegates in C++ (which is what I assume you're using) are a pretty big topic, so I'll link you to the stackoverflow question What is a C++ delegate?

Lets say you have a class that keeps track of the player's score, and whenever an enemy is killed, the score should increase by the enemy's point value.

Pseudocode would look like this:


class ScoreTracker

{

    class EnemyKilledDelegate : Delegate

    {

       int totalPoints;



       virtual void operator()(void* data)

       {

           totalPoints = (int*)data;

       }

    }



    ScoreTracker()

    {

        //...

        EventManager* mgr;

        EnemyKilledDelegate* del;

        mgr.RegisterListener("EnemyKilled", del);

        //...

    }

}

The event manager would then keep some sort of dictionary to call the delegates when the event occurs:


class EventManager

{

    map<string, vector<Delegate*>*> eventMap;  // map strings to (pointers to (vector of pointers to delegates))

    RegisterListener(string event, Delegate* delegate)

    {

        // get the vector of delegates for the event (or create a new one)

        vector<Delegate>* eventListeners = eventMap.getOrCreate(event);

        eventListeners.add(delegate);    // add the delegate

    }



    ProcessEvent(string event, void* data)

    {

        // get the vector of delegates for the event (or create a new one)

        vector<Delegate>* eventListeners = eventMap.getOrCreate(event);



        foreach(Delegate* del in eventListeners)   // loop through and call each delegate

        {

            (*del)(data);

        }

    }

}

And finally, to fire an event:


class Enemy

{

    int pointValue;



    Die()

    {

        //...

        EventManager* mgr;

        mgr.ProcessEvent("EnemyKilled", &pointValue);

        //...

    }

}


Advertisement
From the system implementer side, I recommend a simple function pointer. It allows the most variety to the system users. They can use any of the 8 options in that link.

From the system user side, I'd go with whatever is easiest for that specific object. If they followed the above advice it doesn't matter. Of the 8 options given, both functors and lambdas are USUALLY very easy to implement. If neither is easy in that specific case, then std::bind() is always a handy tool. You also have the option to put in any freestanding function if that makes more sense for the object.

For my GUI system I tend to prefer function objects (i.e. Function<void ()>), I guess there's boost::function and now std::function to choose from, though I use my own implementation.

This allows you to do stuff like:


class ButtonDelegate
{
	public:
	
	Function<void ( Button& )> press;
	Function<void ( Button& )> release;
}

void press( Button& button )
{
}

void release( Button& button )
{
}

void main()
{
	Button* b = new Button();
	
	ButtonDelegate delegate;
	delegate.press = press;
	delegate.release = release;
	
	b->setDelegate( delegate );
}

They might not be the fastest (due to the extra indirection), but they're definitely the most flexible way to do things in a static language like C++. You could also just use regular function pointers, but this would make it trickier to have class instance methods receive events.

You are probably prematurely optimizing here, you should design your system for optimum usability before worrying about the overhead for a function call. It's probably not going to be a performance bottleneck anyway unless you are sending hundreds of thousands of events per second.

You are probably prematurely optimizing here, you should design your system for optimum usability before worrying about the overhead for a function call. It's probably not going to be a performance bottleneck anyway unless you are sending hundreds of thousands of events per second.

It depends on the hardware, but even a few hundred thousand "per second" is not an issue.

In games, you are interested in "per frame", not "per second".

For a 60fps target you have 16.6 million nanoseconds per frame, double that for a 30fps target. That is your maximum time budget.

A few seconds on Google shows that for desktop processors over the last decade --- all with branch prediction, speculative execution, and out-of-order cores --- the average cost of a virtual function call is 2.4 cycles to 4.8 cycles depending on the processor. In nanoseconds that is between 1.1ns and 2.1ns. That is all on a single core, most machines have anywhere from 2-24 CPU cores on them.

Over the years I have spent many months on-and-off during various projects performing optimization passes. In our major titles that can reach high framerates we can easily afford many thousand virtual functions per frame. It is not a performance issue to use them intelligently.

That doesn't mean you can use virtual functions stupidly. They absolutely have a small cost. They require a few nanoseconds. They cannot be inlined, so you must pay the price of calling and returning from a function. It would be foolish indeed to make every function virtual, especially in classes that will never be overridden such as a Vector3 or Matrix44 class. But what are the alternatives when you actually need them? Virtual functions solve very real programming problems.

The first common scenario is that you need to modify execution based on state, also known as dynamic dispatch. Generally the state is a subclass. You can either implement a giant switch statement, or an if/else tree, but neither is extensible. You can either implement your own vtable / jump table, or you can rely on the heavily optimized built in virtual functions.

The second common scenario is that you need to implement interfaces, also known as abstract base classes or pure virtual functions. This is necessary to follow the excellent design principle of dependency inversion. Again you can either implement a non-maintainable switch statement or if/else tree, or you can implement your own vtable / jump table, or you can rely on the built in virtual functions.

Delegates like this are doing exactly the same thing as a vtable lookup. They have essentially the same performance characteristics, although some poor implementations may require a few extra nanoseconds. A cost, certainly, but a trivial one.

Virtual functions, function pointers, and delegates are very useful and solve very real problems. They are already optimized to use less CPU cycles than complex operations like floating point division. Use them.

I am very impressed by std::function.

The most impressive thing is that they allow to match a function prototype even with different capture lists. This means they can have context-dependent "hidden" parameters, which are nothing short of awesome (and basically rape some notions behind functional programming).

Function pointers... mh, I'm no more in that line of thinking. But I admit the degree of compatibility is flat out the best out there!

Previously "Krohm"

This topic is closed to new replies.

Advertisement