vendredi 28 novembre 2014

Abusing static initialization

What we'll be seeing today is a way to (ab)use static initialization to register some classes.

That might come useful when implementing a RTTI (runtime type information) system or when adding scripting capabilities to a framework.

Static initialization


What's static initialization ? Well, basically, when you start a C++ program, main() is not the first thing to happen.

Just before that, a portion of the memory is allocated, which is to contain all the variables that are declared static in your code. Then this memory portion is filled with zeroes, and lastly all the variables declared as static are initialized in there, in a somewhat shady fashion : In a single compilation unit (each .o or .obj file your compiler generates is a compilation unit, so that's more or less be related to a single .cpp file, including all the headers it includes), static variables are initialized in declaration order, but the processing order of the compilation units is undefined.

This means two things :

  • Because of the initialization order, you can't really count on other static variables, or actually on any other variable at all, because main() hasn't started. There is one exception, though : when declaring a static variable inside a function, this variable is initialized on the first call to this function. That will come in useful later.
  • Because the memory is zero-filled before static initialization, you are guaranteed that every single pointer you declare as static will point to NULL when static initialization starts. That could be useful when dealing with linked lists, for example.

Abusing static initialization


The fun thing with static initialization is that you could execute pretty much everything before main happens, even a whole game if you wanted to - though debugging might become a nightmare then.

But let's say we want to add scripting capabilities to our game engine. Let's say we want to be able to declare a class as accessible in the script API and just like that it's useable in, for example, Lua.

I've decided that having to call a function to register the Lua API for every single class in my engine is not only way too verbose for my taste, but also error-prone : if I ever dare to forget this call, the class does not show up in the Lua API, and I'm in for a few tens minutes of debugging, then for a facepalm.

So, basically, using polymorphism to mark a class as available in the script API could be a good start :

class Scriptable {};
class Clock : public Scriptable { /* ... */ };

This doesn't do anything, though. What we want is to call a function that would expose the class' API to Lua :

class Clock : public Scriptable
{
/* ... */
static void ExposeAPI(lua_State* L)
{
    /* Use Lua C API, LuaBridge, Selene... to expose class API */
}
/* ... */
};

We now have the function we want to call on startup, but it's not called yet.

With a bit of help from templates, we will create a class that will act as a proxy. We'll then be able to declare a static instance of this proxy, which will be created at static initialization - and this will call our function automagically ! Let's review some more code :

template<class T> class Scriptable
{
protected:
    struct Proxy
    {
        Proxy() { T::ExposeAPI( /* what to put here ? */ ); }
    };
    static Proxy proxy_;
}
template<class T> typename Scriptable<T>::Proxy Scriptable<T>::proxy_;

Notice the last line. the static Proxy instance is declared inside the class, but we still have to define it - that's what this line does.
Also notice the use of the typename keyword. Because the real type of the Proxy struct depends on the type of T, it is called a dependent type. We need to clarify this for the C++ compiler, thus the keyword.

Now Scriptable is a template class, Clock will need to see its header modified just a bit :

class Clock : public Scriptable<Clock>


This way, we'll have a static Scriptable<Clock>::Proxy instance, that will call Clock::ExposeAPI when initialized at static initialization.

But there's a catch. We need a Lua state to be initialized when exposing a class, and we know that static initialization happens in a somewhat hectic order. How then ? Well, we'll use the fact that static variables in a function are initialized at the function's first call to make a singleton that will initialize our Lua state right when we need it. Let's go !


class Environment
{
    public:
        ~Environment()
        {
            lua_close(L_);
        }

        static Environment& Get()
        {
            static Environment inst;
            return inst;
        }

        lua_State* State() { return L_; }

    private:
        Environment()
        {
            L_ = luaL_newstate();
            luaL_openlibs(L_);
        }

    lua_State* L_;
};


So what happens there ? When we first call Get(), the Environment instance is created, effectively creating and initializing the lua_State - exactly what we wanted.

Let's modify our Scriptable class once again to pass the lua_State to the ExposeAPI calls :

template<typename T> class Scriptable
{
protected:
    struct Proxy
    {
        Proxy() { T::ExposeAPI(Environment::Get().State()); }
    };
    static Proxy proxy_;
}

Now the only thing left is to fill the Clock::ExposeAPI function to expose the Clock API to Lua.

Conclusion


We've used static initialization in such a manner that when the execution of main() begins, your Lua state is already initialized and all the scripting API exposed to Lua. Be warned, though, that doing this could have some unwanted side-effects, in which case, you'd have to resort to more standard manners.

A complete documentation on static initialization is quite hard to find on the internet, and I had to find my way through bits of forum posts and a lot of trial and error to get the wanted result. I hope this article will help document this interesting part of a program's execution :)

You could use this method for anything : exposing a scripting API is an example, declaring metatypes for your classes is another.

The method described here happens to have an interesting compile-time side-effect : if you declare a class as scriptable but forget to create the ExposeAPI static class method, Scriptable<T>::Proxy's constructor will try to call T::ExposeAPI which, of course, doesn't exist, and compilation will fail telling you exactly what's the problem - a rare case when dealing with templates !

2 commentaires:

  1. On stack overflow (http://stackoverflow.com/questions/27672559/using-static-initialization-to-register-classes/27677642#27677642) with some help we found a solution. Afaik getting the code you supplied on this page to behave as expected requires a few non-trivial steps. I'm curious if I just blindly missed something or that the code you supplied in your article is incorrect/incomplete. Could you elaborate on this?

    RépondreSupprimer
  2. I tried this code, but it doesn't seem to work.

    I think the problem is that the static variable:

    template typename Scriptable::Proxy Scriptable::proxy_

    isn't used anywhere. so the compiler skips initialization?

    I tried adding volatile keyword, but it didn't work either.

    RépondreSupprimer