views:

55

answers:

2

I recently read that Java now sports initialisation blocks like the following:

class C {

    public C() { /* Instance Construction */ }
    static { /* Static Initialisation */ }
    { /* Instance Initialisation */ }

}

I was particularly interested in the static block. It got me thinking about the static initialisation order problem that affects many a novice C++ user, and typical workarounds for it, such as wrapping the static member in a free function, or using GNU's __attribute__((init_priority(n))) extension.

I'm looking for some means of writing a method that will be called automatically to initialise the static members of a class, either when the first instance is created or simply at the start of the program, during ordinary static initialisation.

So far this is the best that's come to mind:

class C {
private:

    class Static {
    public:

        Static();

        int i;
        Foo foo;

    };

public:

    static Static statics;

    // ...

};

C::Static::Static() : i(42), foo("bar") {}

That is, wrap all of the static members in a class and create a static instance of that class, whose constructor serves as a static initialisation function. It's simple to alter this to instantiate the statics member only when an instance is created, and even to ensure the proper destruction of static members.

The problem with this, of course, is that C::foo becomes C::statics.foo, which is rather unnatural. Is there a way to get around the awkward syntax, or is there a better solution altogether?

+3  A: 

Seems not worth the effort to me for the most part. You're really not improving readability, the unusual construct is going to confuse future programmers maintaining your code, and the addition of the new class increases complexity, thereby exposing you to the potential for more bugs.

I can see how you rarely might need or want to control the order of static initializations, or for some other reason wrap up all the statics. But from a readability point of view, I prefer the tried-and-true:

class C
{
private:
  static int i;
  static Foo foo;
};

int C::i = 42;
Foo C::foo("bar");

If you've got a lot of those statics, why not send them all off to their own CPP file? But then again, if you really have that many statics, I wonder if there's something wrong with the overall design in the first place...

John Dibling
agree fully, if you are having lots of static in your classes maybe the approach is wrong in the first place.
Anders K.
+1 for last sentence about overall design.
Mark B
Consider it a curiosity. As you say, in some situations it might be useful. The fact is that C++ offers the programmer very little in the way of control over the lifetimes of static instances, and a clean idiom for extending that control can't hurt.
Jon Purdy
A: 

To be clear, there are actually three issues with global variables in general (not only statics):

  • Initialization Order Fiasco
  • Destruction Order Fiasco
  • Multi-Threading Multiple Initializations

Of course, most of the times the destruction isn't so much of a problem, but still it exists.

The new C++0x is thread-aware, and as such there is some gain (especially for local static variables regarding the multi-threading issue).

With the advent of C++0x, the following code may only suffer from the "Destruction Order Fiasco"... unless you've got a cyclic reference in initialization of course.

// foo.h
class Foo
{
public:
  static Foo& Instance() { static Foo M; return M; }

private:
  Foo();
};

// bar.h
// idem for Bar

// foo.cpp
Foo::Foo() { Bar& bar = Bar::Instance(); .... }

This works fine: since the instance is created on demand, the language guarantees it's there when you need it, and with C++0x guarantees the behavior even if multiple threads try to access the function at once.

Now, what of the "Destruction Order Fiasco" ?

Well, objects are destructed in the reverse order of their construction, therefore if you need to access an object in your destructor, access it in your constructor just to make sure it's built before you are, and everything is fine.

Of course, without C++0x things are a bit hearier. To avoid the multi-thread issue, the only advice is to access all the variables first while the application is still single threaded (in main). This way all the instances are created and there's no longer any concurrency issue.

Matthieu M.