Wednesday, September 24, 2008

Callbacks, the trick pointer you execute

At work I tend to develop programs using C#, which I don't particularly like but it does have some neat features. One of the features that caught my attention are delegates. Delegates are essentially function pointers (actually they do a little more than that). A function pointer is just a pointer to a function much like a pointer to a variable. I found it extremely interesting how delegates are used in ASP .NET for event driven processes.

I began wondering if I could implement similar systems in my own projects using C++.

Callbacks in C++ provide a way to call functions by executing function pointers. In C# delegates are type safe, this helps ensure you don't end up passing and receiving junk data to and from your function. Callbacks are not quite as nice, they are not necessarily typed and usually make heavy use of void pointers and unsafe type casting. To achieve typed callbacks in C++ I started making a templated callback class that allows for typed arguments and typed returns. Using a templated library helps ensure type safety and provides excellent code reuse when using callbacks with various types. Using the templated callback library, making a callback is as easy as...


void myFunction(int x)
{
...
}

Callback1<int> myCallback( myFunction );

Executing the callback is then as simple as

myCallback(7);

Why would we want to go through all this trouble just to run myFunction? Well, think about a system that executes functions but the functions are not known until runtime. Event processing tends to be one such system.

For event processing you could have a function that contains one giant if else or case statement for every possible event. Or you could make a system where you register functions with a type of event, and when the specific event occurs, a callback to your function is executed. This type of event system is exactly what I used callbacks for.

In the Stepmania Online game server I am developing, players are able to enter commands from the chat interface to manipulate the server. Since the events the server is handling are text strings, a switch statement is not an option. With a couple commands, multiple if else statements is an ok solution. However once the number of commands begins to grow, the if else statements become very long and ugly. To solve this I made a list of callbacks, each associated with a string command. I then "registered" each function with a string command by creating a callback to the function and adding it to the list along with the associated string command.

When a command is received, the program simply loops through the list of command stringsand callback. When a match is found between the stored string and the command, the callback associated with the command is executed. No giant if else statement required!

The real power in this system is that extra commands can be created by just compiling and linking in extra commands. This means, the game server could be released as a library and the chat commands can be expand uppon without touching or recompiling the core of the server. Very useful!

I have found that it takes a while to fully grasp the concept of callbacks, but it is well worth the effort.

Thursday, September 4, 2008

Revisiting old code

This past week I began working on Mercury2, which will be almost complete rewrite of my first game engine Mercury. So far I have only worked on the render window. While working on it i decided to look at my old code from Mercury1 to try to figure out how I had done it. When looking back at old code, its amazing to see how much your coding style changes and improves over the years. Its been over three years since I wrote the windowing code for Mercury and it certainly shows.

Looking at my old window code, it was clear I didn't have any understanding of what I was doing when I wrote it. Actually a lot of it was taken (and giving credit to) from NeHe lesson 1 and was modified to be a little more flexible (in my mind at the time). The result worked well, but the code was a pretty convoluted. I decided it was best to sit down and review my old code to try to figure out exactly how it was supposed to work. So with a long of review of my old windowing code, and some heavy reading of MSDN documentation I developed a much more solid window system.

Moving forward in the project I think it will be difficult to properly design the project knowing I have large amounts of "old code that works" that I could fall back on.