Engineering Logs - Singletons Revisited

Posted by Astryl on Feb. 26, 2015, 3:28 a.m.

Now that I have free time again for my own programming projects, I decided to start working on games again, and thus on my game engine.

My current goal is to transfer the gameplay from that puzzle/RPG game that I was working on in collaboration (Which is on halt due to the guy I'm working with being bogged down by the German education system).

So yeah, I'm quietly porting it to C++; we don't want to use GM for it, because it's damned messy.

Anyway, the last blog I wrote about Singletons showed me doing it in the naive way: Manually creating classes 'as' a Singleton type by giving it specific properties.

That works, but is a slog. Wouldn't it be far easier to just inherit some sort of global "Singleton" type and not have to do anything further?

Well, we can. And it's easy too.

There are two methods I'm going to address: One for those who like templates, and one for those who don't.

The Template method

Templates are a very powerful part of the C++ language… but in my personal opinion, they can also be messy, unwieldy abominations that make your code look like it fell part-way into a mincer.

It all depends on how you use them, of course.

Originally, I was just going to write the quick and dirty 'macro' technique, as below, but then on the spur of the moment I added this as an elegant alternative.

template<class ATYPE>
class Singleton {
    public:
        static ATYPE & getInstance() {
            static ATYPE singleton_instance;
            return singleton_instance;
        }
};

And that is pretty much it. Define your classes as such:

class Tester : public Singleton<Tester> {
    private:
        Tester(){} // Prevent construction
    public:
        void doSomething() { 
            printf("Something is being done\n");
        }
};

And use it like this:

int main(int argc, char** argv) {
    Tester::getInstance().doSomething();
}

Or, as I like to do it:

#define INST(singleton) (singleton::getInstance())

int main(int argc, char** argv) {
    INST(Tester).doSomething();
}

Works like a charm, easy to use, and easy enough to understand.

The Black Magic method (Macros)

First things first, Macros aren't by any means "bad". But there are "bad" ways to use them.

The C Pre-processor is a very powerful tool in the right hands; I use it to process my game scripts (Whether LUA or Squirrel), allowing me to use the ever-useful #include directive, as well as macros.

Macros, when used incorrectly, can cause all kinds of headaches for the developer; so use them at your own risk.

This, however, is an interesting little use for the things.

#define DECL_SINGLETON(stype) static stype & getInstance() { static stype singleton; return singleton; }
#define INST(singleton) ((singleton&) singleton::getInstance())

class Test2 {
    private:
        Test2(){}
    public:
        void doSomething() {
            printf("Something else is being done!\n");
        }

        DECL_SINGLETON(Singleton)
};

int main(int argc, char** argv) { 
    INST(Test2).doSomething();
}

Arguments for and against either method

Both of these methods work; they are almost equal in performance, identical in use… it's just a case of how you want to create your singletons.

Personally, I'm a fan of simply inheriting from the Singleton type. It's clean, elegant and easy.

On the other hand, you may be working in an embedded environment where templates may not be supported (Unlikely as that may be. Embedded systems are pretty sophisticated these days). Or, you know, you're using wxWidgets and already have a million macros sprinkled throughout your source.

So basically, flip a coin.

Footnote

I'll probably be posting a few updates on that game, specifically on the 'porting' process, within a week or two.

Though porting is a bit of a grand word to use; it's like creating a whole new game; I'm just reusing the art and game rules. :P

Comments

DesertFox 9 years, 6 months ago

C++ is the glamourous rockstar that everyone pays attention to. C is her plain-looking older sister that does all of the songwriting :(

sirxemic 9 years, 6 months ago

Quote: Mega
C++ is a programming language, JavaScript is a scripting language.
I know you didn't mean it this way, but when you say JavaScript isn't a programming language, it sounds very derogatory. Javascript *is* a programming language, just not as low-level as C++.

Astryl 9 years, 6 months ago

The way I mean it is that, generally, C++ is a language that works "with the metal", whereas Javascript works with a context (Usually a DOM of some sort, though Node mixes that up a bit).

Of course, generally speaking, any scripting language is a programming language. I just tend to make a clear distinction between them and languages like C++, Java or C#.