Dave
Dave

Reputation: 1659

Static Initialization Order in C++

A common soution to the static initialization order problem in C++ is the "construct on first use" idiom. This idiom puts a function wrapper around your static object.

Without the idiom, you'd have:

Foo bar;

With the idiom, you'd have:

Foo &bar()
{
   static Foo *ptr = new Foo();
   return *ptr;
}

Moving from the first to the second requires that all uses of bar change from bar to bar(). I'm in a situation where I can't make this change (far too many use sites, lose naturalness of use of operator<<). I've tried all sorts of syntactic contortions to find a way to implement this idiom that doesn't require that the call sites change. I cannot find one. Does anybody in the community have a trick up their sleeve to allow this?

Thanks, Dave

Upvotes: 3

Views: 1031

Answers (5)

Gigi
Gigi

Reputation: 4962

You can rename Foo to something like FooImpl, keeping the "construct on first use" idiom. Then:

struct Foo
{
  Foo()
  : _impl(FooImpl())
  {}

  // wrappers for the FooImpl methods
  bool my_foo_impl_func()
  {
    return _impl.my_foo_impl_func();
  }


private:
  FooImpl& _impl;
};

With this wrapper the rest of your code don't need to be changed.

Upvotes: 1

Loki Astari
Loki Astari

Reputation: 264331

The simple solution is: Assuming you can re-compile all your code.
But if you can't do that most solutions here are going to fail.

Assuming your old Foo looks like this:

Old Foo.h

extern Foo  bar;

Old Foo.cpp

#include "Foo.h"
Foo& bar;

Then you can replace this with:

New Foo.h

Foo& getBar();

// Not like I like this but it would be better to force users to change
// how they use bar() rather than use this hack.
#define   bar    getBar()

New Foo.cpp

#include "Foo.h"
Foo& getBar() { static Foo bar; return bar; }

Upvotes: 0

Loki Astari
Loki Astari

Reputation: 264331

Simpler variant of James Kanze solution.

But fails for the same reasons:

Foo.h

class Foo
{
};

Foo& getBar();

namespace
{
    Foo& bar = getBar();
}

Foo.cpp

#include "Foo.h"
Foo& getBar() {static Foo bar; return bar;}

Now in every file that includes Foo.h we introduce an anonymous namespace that initializes a local object bar (that is a reference). So it is initialized before you can use bar (as you have to include Foo.h to know what Foo objects are to use them).

The reason this (and James Kanze) solution can fail is:

You need to a level of indirection to make it fail:

Bar.h

class BarBar
{
    public: BarBar();
}

BarBar& barbar();

Bar.cpp

#include "Bar.h"
#include "Foo.h"

BarBar& barbar() { static BarBar b; return b;}

BarBar::BarBar()
{
    // Here Bar is a Foo
    bar.somFooThing();
}

// This works because you have to include Foo.h
// Which makes sure that bar is initialized correctly because the
// order of initialization inside the compilation unit is defined.
BarBar  barGlobal;

FOOOOOO.h

class FOOOOOO
{
     public:
        FOOOOOO();
};
FOOOOOO.cpp
// Notice we don't need to include Foo.h we are not using it directly.
#include "Bar.h"

FOOOOOO::FOOOOOO()
{
      barbar().doBArStuff();     
}

// Here is where the problem occures.
FOOOOOO  FCUK_UP;

// If this object is constructed first.
// It will call barbar() in its constructor.
// Which will initialize an object of Foo in its constructor.
// But at this point we have not forced any initialization of `Foo bar`

Upvotes: 0

James Kanze
James Kanze

Reputation: 153889

It's far from perfect, but you can always do what the implementations of iostream do to ensure the initialization of std::cin and std::cout. (This is sometimes known as the nifty counter idiom, or the Schwarz counter, after the inventer of the technique.) There are several variants, but the basic idea depends on the fact that order of initialization is guaranteed within a single translation unit, so if you define a (static) instance of some special type in your header, it will be (normally, since headers are included at the top) constructed before anything in the source source file. The constructor of this static instance checks a global flag or counter; if the value is 0, it initializes your global object, and increments the counter so that following constructors won't initialize it. (There's no order of initialization problem for the counter, because it depends on zero initialization.) The only problem is how to declare the object itself. I think in the earliest versions, it was declared in assembler, as an array of enough bytes. What I've found to work (although not guaranteed by the standard) is to declare a special, no-op constructor, and invoke that in the "initialization" of the variable, And of course, the initialization objects use placement new.

This may be clearer with a quick example:

Foo.hh:

class Foo
{
    enum Hidden { noop };
    Foo( Hidden ) {}
public:
    Foo();    //  The real constructor.

    static Foo bar;

    class FoobarInitializer
    {
    public:
        FoobarInitializer();
    }
};

static FoobarInitializer initializeFoobar;

Foo.cc:

namespace {

int initCount;
}

Foo Foo::bar( Foo::noop );

Foo::FoobarInitializer::FoobarInitializer()
{
    if ( initCount == 0 ) {
        new (&Foo::bar) Foo();
    }
    ++ initCount;
}

This technique works equally well if bar isn't a member of Foo, but you'll need to make more things public. (The initializer could be a friend, but at the least, Foo::noop must be public.)

I'll repeat that this is not guaranteed: Foo::Foo( noop ) may be called on bar after the initialization class has constructed it, and an implementation is allowed to scribble over the memory before entering the body of the constructor. But it's always worked in practice for me, and I've used it with a number of different compilers.

Upvotes: 2

mcnicholls
mcnicholls

Reputation: 846

Can't you just:

Foo& bar = bar();

somewhere and carry on using bar?

Also why not implement the idiom as:

Foo& bar()
{
    static Foo foo;
    return foo;
}

Upvotes: 0

Related Questions