Reputation: 1659
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
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
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.
Old Foo.h
extern Foo bar;
Old Foo.cpp
#include "Foo.h"
Foo& bar;
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
Reputation: 264331
Simpler variant of James Kanze solution.
But fails for the same reasons:
class Foo
{
};
Foo& getBar();
namespace
{
Foo& bar = getBar();
}
#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:
class BarBar
{
public: BarBar();
}
BarBar& barbar();
#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;
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
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
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