Feeds:
Posts
Comments

Archive for November, 2012

Using the STL pt. 2

So having covered a better way to remove and erase items from a container, there are a couple of other ways that I’ve leveraged the stl to improve my game.

One is through using std::copy to write out to a file, instead of using an iterator and a for loop. So for instance the old way I used to write out to a file was

Std::ofstream outf("high scores.txt");

for(std::vector::iterator it = hiscores.begin(); it != hiscores.begin() + 8; ++it)
{
    Outf << (*it) << std::endl;
}

Which wrote out the first 8 elements of the hiscores vector to the high scores file (at the end of a game, after the users score had been added into the vector and sorted). This worked fine but I came to realise that there was a better way after a bit of exploration on StackOverflow. So the code now reads like this

Std::ofstream outf("high scores.txt");

Std::copy(hiscores.begin(), hiscores.begin()+8, std::ostream_iterator(outf, “\n”));

Which to my eyes looks a lot nicer and can be optimised by std::copy.

Within the original for loop I also used to check to see if the user had a new high score with a basic if statement. With that for loop now gone I obviously need to put that check back in elsewhere and to do that I’ve used std::for_each, as below

Std::for_each(hiscores.begin(), hiscores.begin()+8, [this](int i){ checkNewHighScore(i); });

//outside, as a separate function
Void GameOver::checkNewHighScore(int i)
{
    If(player.getTotalScore() == I)
    {
        NewHighScore = true;
    }
}

With newhighscore being a bool contained within GameOver. For_each is using a lambda function to capture the int in hiscores that the iterator in for_each is currently pointing at and then passes that int into checkNewHighScore. All contained in one easy to read line of code.

The final example of using the stl in my GameOver class relates to using std::sort (I’m not sure how I only managed to reference one function in this post, especially as I’m referencing the use of the various stl algorithms in the reverse order to which they’re called!). Obviously once a user has achieved a score that is added into the high scores table and then we reorder the scores around it. I could have done this myself, but why bother when the stl has done it already and implemented it. I used to call this with a lambda function, but recently found a call that would do the ordering correctly for me, std::greater


Std::sort(hiscores.begin(), hiscores.end(), std::greater());

And that’s it, my high scores are ordered from biggest to smallest, at which point I can start finding out if the user has a new high score and writing them back out to the high scores file.

Read Full Post »

Using the STL properly

So in my quest to learn C++ and perfect the code that my games are running, I’ve come across a lot of people who say that most C++ programmers are just writing C with classes and one of the best ways to take advantage of the language is to use the STL. The main reasoning is that the STL was written by the best minds in programming and has been made as efficient as possible, so using them will almost always be quicker than writing your own loops.

Take for instance removing objects from a vector. In my code there was a lot of this kind of code:

for(std::vector<Object*>::iterator i = objects.begin(); i != objects.end();)
{
    //process the objects

    if((*i)->isDead())
    {
        delete *i;
        i = objects.erase(i);
    }
    else
    {
        ++i;
    }
}

Which I used to think was really efficient, looked quite organised and was easy to follow. Unfortunately, it’s not that efficient at all. Whenever erase is called, every member of the vector that is after the element erased has to be moved up one space to fill in the gap. In my ParticleEngine class I’m managing 400 particles, of which 40 are going to be dead at any one time. That means that 40 times in one pass through that for loop a couple of hundred objects are having to be reassigned positions in memory, which is obviously going to be a massive slowdown and hog on performance. This was showing itself up in rudimentary testing when I’d see a jump in CPU usage on task manager whenever a ParticleEngine was called into being.

Needless to say I’ve now found a better way and the worst part is that the answer was under my nose the whole time. Scott Meyers’ book Effective STL documents the best way to erase elements from a container and needless to say, it isn’t the way above. He recommends using std::remove followed by the container’s erase member function. std::remove takes the elements to be removed and moves them to the end of the vector, returning an iterator to the end of the range that are not to be removed (or the beginning of the range of elements that are to be removed). Erase then uses this as it’s starting point and erases all elements from this point to the end of the original container. So how do we know which elements to remove/erase is the next question, leading on to another neat trick from Meyers’ book. Any pointers that aren’t needed any more can be deleted and set to NULL, that way all we have to look for is NULL pointers and mark all of these for erasing. Hence the following code can now replace the above:

void processObject(Object* o)
{
    //process o

    if(o->isDead())
    {
        delete o;
        o = NULL;
    }
}

//go through objects, processing each one
std::for_each(objects.begin(), objects.end(), processObject);

//remove any NULL Object*, then erase them from objects
objects.erase( std::remove(objects.begin(), objects.end(), static_cast<Object*>(NULL)), objects.end() );

This code requires one loop through the container for for_each, another for remove and then a loop through however many objects are to be erased. So for my ParticleEngine example, I’ve gone from reorganising a vector of 400 items up to 40 times a loop to once in std::remove. Much quicker and nicer.

Read Full Post »