The Developer’s Cry

Yet another blog by a hobbyist programmer

A bit of C++ for Objective-C programmers

With the advent of Apple’s Swift, and it’s apparent success, Objective-C is quickly becoming a dying language. Good riddance, but what about all those codes that were written in Objective-C? Clearly Apple expects you to switch to Swift, and for Cocoa or iOS codes that is a sensible choice. It just so happens that I have some game codes written in Objective-C, and I’m personally not all that crazy about Swift. OK, Objective-C isn’t going away (yet), but it’s interesting to see what that code would be like when rewritten in C++.

I actually do like the Objective-C language, and I think it’s a pretty good fit for programming games. The reason for this is that it is C with classes. In my opinion, C with classes is the holy grail of programming. C++ also adds classes to C, and a whole lot more. It is a big language with many features and sometimes horrible syntax, making it a difficult language to learn. We can start off easy and build it from there.

In Objective-C you have learned to use the Foundation classes; everything is an object, notably an NSObject. Objective-C code typically leans heavily on classes that are provided by frameworks. One reason for this is the reference counting system, which can only work if every class inherits from NSObject. They go as far as to put integers and floats into NSNumber objects, just to be able to store them in an NSArray.
For C++ and especially for game codes I like adopting a code style that is closer to the metal, closer to C. Therefore the C++ classes that we use will mostly be defined by ourselves, and sometimes provided by the STL (Standard Template Library).

Let’s have a look at some Objective-C game code and convert it by example.

// Player.h (Objective-C)

#import "Monster.h"
#import "Color.h"

@interface Player : NSObject {
    Monster     *ship;
    Color       *color;
    NSInteger   score;
    NSDate      *timeOfDeath;
}

@property (retain) Monster *ship;

-(void)move;
-(BOOL)dead;
-(BOOL)collideWithObject:(WorldObject *)o;
-(void)draw;

@end

extern Player *player;

So we have our Player class, which is basically a Monster with its own color and score. By the looks of it, Player could have been a subclass of Monster, but I didn’t go that route here. The idea is that the Player controls a ship, and the ship is so much like a Monster, that I chose the ship to be a Monster.

As always in Objective-C, the class inherits from NSObject. We see NSInteger and NSDate. Let’s stick to good old C’s int and time_t for the C++ version.

The ship property is retained. C++ has no such thing, as it does not do reference counting like Objective-C. (* It does have ‘smart pointers’, but more on that later. For now I will stay away from those). A rather straightforward translation of the code to C++ would be:

// Player.h (C++)

#include "Monster.h"
#include "Color.h"

class Player {
public:
    Monster *ship;
    const Color *color;
    int score;
    time_t timeOfDeath;

    Player();
    ~Player();

    void move(void);
    bool dead(void) const;
    bool collideWithObject(const WorldObject *o);
    void draw(void) const;
};

extern Player *player;

Player() and ~Player() are the constructor and destructor. The constructor is like init, and the destructor is like dealloc. I say “like” because the two are not identical. They aren’t identical because C++’s inner workings are different from Objective-C’s.

What are all those const keywords doing? Remember that Objective-C has a immutable objects? A const object is almost the same, but it is more like a promise that we will not change the object. A const member function promises not to change this object (the self, in Obj-C speak). If that promise is broken, the compiler will error out, but it is a promise that may be casted away if you really want to. You might even do without const at all as it tends to get in the way when you don’t rigorously pursue it.
I set the color member to const here because it wasn’t a property in the Objective-C interface, so it probably was just a raw pointer to a static object.

Before we move on to the implementation, I would like to mention that we have the option of doing things quite differently, much more C-plus-plussy. However, the closer we stick to the Objective-C declaration, the fewer changes need to be made to the implementation.

// Player.m (Objective-C implementation)

#import "Player.h"

Player *player = nil;

@implementation Player

@synthesize ship;

-(id)init {
    self = [super init];
    if (!self)
        return nil;

    ship = [[Monster alloc] init];
    color = [[Color alloc] initWithRGBA:0xff0000ff];

    return self;
}

-(void)dealloc {
    [ship release];
    [color release];

    [super dealloc];
}

-(void)draw {
    [ship draw];
    [scoreboard drawScore:score];
}

We see here that color wasn’t just some static pointer, but apparently it didn’t need any setters/getters so it was not a synthesized property.

The C++ code is much the same, but note that it doesn’t use super. In this case, we don’t use any inheritance at all. If we did, then we would have to invoke the superclasses constructor. For the destructor this invocation is implicit.

// Player.cpp (C++ implementation)

#include "Player.h"

Player *player = nullptr;

Player::Player() {
    ship = new Monster();
    color = new Color(0xff0000ff);
    score = 0;
    timeOfDeath = (time_t)0L;
}

Player::~Player() {
    delete ship;
    delete color;
}

void Player::draw(void) const {
    ship->draw();
    scoreboard->drawScore(score);
}

The constructor/destructor pair are so simple, they may be inlined in the class declaration in the header file.

Now, you may run into trouble because delete is not the same as release. C++’s raw pointers are not reference counted, so delete will delete the object right there, regardless of whether the pointer was still in use. It is up to the programmer to get this right — just as it is up to the programmer to get the number of retain/release calls right — but read on.

Let’s stir things up a little. Let’s have a look at some explosions:

void init_explosions(void) {
    all_explosions = [NSMutableArray arrayWithCapacity:NUM_EXPLOSIONS];
}

void reset_explosions(void) {
    [all_explosions removeAllObjects];
}

void add_explosion(WorldObject *o) {
    Explosion *x = [[Explosion alloc] initWithOriginal:o];

    [all_explosions addObject:x];
    [x release];
}

So, we have an array that holds our explosions. In C++ we could use a simple static array, or we can use the STL vector class. An std::vector is a growable array, that may be initialized to have a certain capacity. Since our array won’t grow beyond that maximum capacity, we might also choose an std::array. There is more than one way to do it.

The vector is a template class, which means that you can make vectors for any given type. But unlike an NSMutableArray, a vector can not hold mixed types. [This statement is more or less false … because we are storing pointers to Explosions, we can also store any subclass of Explosion because of polymorphism. And this is exactly the same in Objective-C, where you are effectively storing subclasses of NSObject].

#include <vector>

void init_explosions(void) {
    all_explosions = new std::vector<Explosion *>(NUM_EXPLOSIONS);
}

void reset_explosions(void) {
    // bug: memory leak here
    all_explosions->clear();
}

void add_explosion(const WorldObject *o) {
    Explosion *x = new Explosion(o);

    all_explosions->push_back(x);
}

C++ has no retain/release, so we here we have a memory leak on our hands. The clear() method will clear the array vector, but it will not delete the stored explosions (!)
We can fix this by calling delete on each and every element, or we can use a technique that is more akin to release: by using a smart pointer. A smart pointer knows when memory should be freed, and takes care of this automagically.

#include <vector>
#include <memory>

void init_explosions(void) {
    all_explosions = new std::vector<std::unique_ptr<Explosion> >(NUM_EXPLOSIONS);
}

void reset_explosions(void) {
    all_explosions->clear();
}

void add_explosion(const WorldObject *o) {
    auto x = std::make_unique<Explosion>(o);

    all_explosions->push_back(std::move(x));
}

This code does not leak because the unique_ptr object will free the memory once the smart pointer itself is destructed. Here we use unique_ptr, because the object is not shared. Objective-C’s retain acts like shared_ptr, which has a reference count.
A unique_ptr does not share, so you even can not assign it to another variable. That’s why we move the explosion object from variable x into vector all_explosions.
Personally I’m a very classic C programmer and I’m not fond of the smart pointer syntax, so I usually stick with regular, raw pointers, at the risk of having leaks.

The C++ language allows you to use object instances rather than pointers to instances, which is a game changer. For example, we could have written the above explosion code as:

#include <vector>

std::vector<Explosion> all_explosions(NUM_EXPLOSIONS);

void reset_explosions(void) {
    all_explosions.clear();
}

void add_explosion(const WorldObject& o) {
    Explosion x = Explosion(o);

    all_explosions.push_back(x);
}

Hurray, no more pointers! Told ye C++ was nice. This code has now been simplified so much, we don’t even need the vector class anymore, and could easily do with a simple C array.

As you can see, C++ allows instances of objects to be constructed on the stack. This will implicitly invoke the constructor, and it will implicitly destruct the object when it goes out of scope (because the stack is being unwinded). Thus you use objects in a very natural way, the same as any other variable—unlike Objective-C, where every object is a pointer to the instance. In terms of performance, copying objects is more expensive than shuffling pointers around. The C++ compiler can optimize certain cases however so it may eliminate certain copy actions.

C++ is more rigid than Objective-C. For example, you can not dynamically extend a class (categories) and there are no protocols in the way that you are used to in Objective-C. While C++ is not as dynamic, in return you get higher performance.

There are lots of things I didn’t touch upon because C++ is such a versatile (read: large) language. Most notably, operator overloading. Operator overloading is ideal for matrix math, 2D and 3D vector classes. C++ is a rich language that is maybe too powerful for its own good … which is to say, you can make things really hard on yourself by getting too much caught up in unnecessary complex constructs. Keeping things simple allows you to maintain a good overview of the code. This alone can be a challenge in itself. Game programming is hard enough as it is already.