3

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

Mysticial
  • 464,885
  • 45
  • 335
  • 332
Dave
  • 1,519
  • 2
  • 18
  • 39
  • 2
    Normally the static member is not a pointer but an object. That way you get automated destruction. – Martin York Feb 03 '12 at 16:58
  • @LokiAstari In most cases, you don't want the destruction, since that results in an order of destructor problem. – James Kanze Feb 03 '12 at 17:07
  • 1
    @JamesKanze: I totally disagree. `Normally` you do want destruction. (not wanting destruction is the exception). That is why this idiom normally uses an object. The order of destruction is well defined as the inverse of the order of construction so there should never be a problem. See: http://stackoverflow.com/a/335746/14065 – Martin York Feb 03 '12 at 17:10
  • The real solution is to change the code. Or never use global state. – Martin York Feb 03 '12 at 17:11
  • Does Foo have any methods? Or is it just passed as a parameter when used. In the latter case you can hack a new Type that out-coverts when used as Foo type. Unfortunately (for this situation but in general fortunately) you can not override the `.` to do something useful. – Martin York Feb 03 '12 at 17:18
  • @LokiAstari Normally, you want destruction. Most singletons are an exception, however. And the fact that the order of destruction is the opposite of the order of construction doesn't prevent order of destruction issues. (Note that `std::cout` and `std::cerr` are never destructed. Precisely for these reasons.) – James Kanze Feb 03 '12 at 17:42
  • @JamesKanze: I totally disagree (for Singeltons they are not exceptions to the rule) for exactly the same reasons as above. Also I have not read anywhere in the standard that cout/cin are not destroyed. Please provide a reference (as I believe that statement is not true). Correct the defined order of destruction does not prevent problems (if you are a beginner (but that is true for nearly anything non trivial in C++ (which is everything))). But if you know what you are doing it is not an issue because you know the order and follow one simple rule. See the link provided. – Martin York Feb 03 '12 at 17:51
  • Regarding the destruction of the standard stream objects: §27.4.1/2 "The objects are not destroyed during program execution." With a footnote which gives as a reason the exact reason I've said. If an object needs destruction, maybe it shouldn't be a singleton. (Although I don't really agree with that either. There are singletons which you would want to destroy.) – James Kanze Feb 03 '12 at 18:31
  • @LokiAstari With regards to managing the order of construction/destruction: you can't, really. At least not if you are writing library code, which will be used by others. – James Kanze Feb 03 '12 at 18:33
  • @JamesKanze: Order of destruction. You can't force good behavior in destruction. Just like you can force people to use the rule of three (five) on their objects. But you can do it correctly for code so that when people do it correctly themselves and use your library it is not your code the messes up. – Martin York Feb 03 '12 at 19:00
  • @LokiAstari There's a clear difference in the two cases. The rule of three applies locally, to your class. What we're talking about here is creating unnecessary, unexpected and counterintuitive constraints for the users of your class. (Thank goodness the authors of the standard library didn't take this position with `std::cout` and `std::cerr`.) – James Kanze Feb 06 '12 at 13:06
  • @JamesKanze: There you are wrong. And your attempt to claim that this is the reason the standards committee treats std::cout this way is disingenuous (see below). Also you attempt to categorize the techniques as different is a masterful attempt at misdirection but totally false. The technique (as is the rule of three) is one that can be applied solely by the user of your library. – Martin York Feb 06 '12 at 14:43
  • @JamesKanze: The reason the std::cout and family is done this way is for safety. The problem is the mixed technique (global state and global functions generating global state); As I have shown below with your code is that it can fail under certain circumstances. If yo do away with the global instances and use the static member of a function idium then you can correctly control the creation and destruction order completely. Unfortunately the standard library designers did not have that luxury (for backward compatibility reasons) thus safety must be their only goal. – Martin York Feb 06 '12 at 14:44
  • @JamesKanze: As a result the goal of currently library designers can achieve a totally safe construction/destruction order as long as you follow one rule. And yes the rule I describe applies solely to the user of your library just like the rule of three. Though like the rule of three you must also follow it in your library. – Martin York Feb 06 '12 at 14:52
  • @JamesKanze: I think your description of the constraints as `unnecessary, unexpected and counterintuitive` as just silly and bombastic nonsense. They are no worse than the rule of three constraints: (1) Try not to use global accessible mutable state. (2) If you must; use it via the static member of a function idiom. (3) If you accesses global mutable state from the destructor you must use it in the constructor as well. – Martin York Feb 06 '12 at 14:57
  • @LokiAstari "The reason the std::cout and family is done this way is..." That's not what Jerry Schwarz said, and he's the author of the code. As for the rest, I'll let the readers decide (although the difference between the rule of three and adding artificial constraints to the **use** of a class is a rather fundamental one in software engineering). – James Kanze Feb 06 '12 at 15:12
  • @JamesKanze: Again you misrepresent what I am saying (I don't know if I am being unclear or you are deliberately trying to obtuse and twist my words). But like the the rule of 3. This rule **does not** affect the **use** it affects **how a class is defined**. Which is **exactly** the same concept that is used in the rule of three. The user of the class should be unaware any constraints. But the designer of a class should make sure that they designs the class so that it can not be misused. – Martin York Feb 06 '12 at 16:08
  • @LokiAstari Whether I can use a static instance of a class in the destructor of some other class (which may have static instances) is an important part of the external interface. Because of potential order of destructor problems, one generally assumes that one can't. But for specific objects with static lifetime, it's useful to add this guarantee; `std::cerr` is an obvious example, but in my experience, it applies to most (but not all) singletons. – James Kanze Feb 06 '12 at 16:23
  • @JamesKanze: It is obvious now that you are not reading what I have written as that is just pure herring bate. Please feel free to Troll on by yourself. – Martin York Feb 06 '12 at 16:25
  • @LokiAstari So, how much of this is *directly relevant to the post*? Could we clean up the comment thread a bit and possibly update the questions/answers? – casperOne Feb 06 '12 at 16:31
  • @JamesKanze So, how much of this is *directly relevant to the post*? Could we clean up the comment thread a bit and possibly update the questions/answers? – casperOne Feb 06 '12 at 16:31

5 Answers5

2

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Unfortunately that can fail: File A.cpp: `A a;` It noes nothing about Foo but uses the global object bar defined in Bar.cpp. `Bar& bar() {static Bar b; return b;}` In the constructor of Bar it tries to access `Foo bar;` which may not have been constructed at this point as this set of calls was initiated in the compilation unit A.cpp what may not have imported Foo.hh (it just needs to import Bar.h). To make sure this works you must include Foo.hh from all header files that use Foo in any method (but this is not required as you may jsut included in bar.cpp). – Martin York Feb 03 '12 at 18:01
  • A simpler version of this technique exists where you use an anonymous namespace to force initialization of a local version of bar (which is a reference). But it fails for exactly the same reasons as above. See http://stackoverflow.com/a/9133447/14065 – Martin York Feb 03 '12 at 18:18
  • @LokiAstari It can fail, yes. Just as using `std::cout` in a constructor of a static object can fail. But it's about the best you can do, and at least you can specify what the client code has to do to prevent it from failing. – James Kanze Feb 03 '12 at 18:35
1

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.

Gigi
  • 4,953
  • 24
  • 25
0

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;
}
mcnicholls
  • 846
  • 5
  • 10
  • Unfortunately that still fails as the reference may not have been initialized before use in the same way that a variable may not have been initialized. – Martin York Feb 03 '12 at 17:03
  • Yeah not sure what I was thinking there. Could be really ugly and define a macro bar that resolves to bar(). I can't believe I have suggested it though, very nasty hack. Personally I would do a search and replace. – mcnicholls Feb 03 '12 at 17:07
  • #define Foo Foo() as suggested by mcnicholls seems to work perfectly well. I too normally cringe at the use of macros on principle, but I can't seem to find a case where this fails. – Dave Feb 03 '12 at 18:52
0

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`
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • It's not quite the same thing. If you're defining a new interface, you wouldn't bother; you'd use the much more robust singleton idiom, and the client code would write `bar()`. The only time you'd use anything like this is when implementing a pre-defined interface, like `ostream`. And for that, your solution fails, since it doesn't guarantee that `&bar == &bar` (when one or both have in fact been passed in as an argument). – James Kanze Feb 06 '12 at 17:01
  • /@JamesKanze: I know this fails that was the point of this post. And it fails under exactly the same circumstance as your code (which is what I am trying to explain). Neither method is robust. – Martin York Feb 06 '12 at 18:42
  • But there isn't anything better. And the problem is well known, at least to anyone who uses iostream, since the result is the same as for `std::cout` and `std::cerr`. It is, in the end, a standard C++ idiom. – James Kanze Feb 06 '12 at 18:46
  • @JamesKanze: The above code does guarantee that `&bar == &bar` because `bar` is a reference (and thus does not have an address) and gets it value from `getBar()` and the one definition rule guarantees that there is only one version of `getBar()` and thus its result. Also the above is to get around the problem of having a global variable rather than using a static variable inside a function.The standard has no choice (for backward compatibility). New library developers have a choice.This is to show why (this and your) are a bad choice. The problem is soved by using only static function members. – Martin York Feb 06 '12 at 19:00
  • @JamesKanze: Yes there is a common idiom to solve this problem. It is **NOT** what you propose. Yours is an outdated idiom to get around a problem that should not exist (and does not when you write code correctly). But we are getting off topic. As the question is about using the broken technique of a global variable and its initialization order without using the correct idiom. – Martin York Feb 06 '12 at 19:02
  • OK for the address. I read too quickly. I'd still use the nifty counter, probably, because it's a standard idiom, which will be immediately recognized by any experienced C++ programmer. But it's not a big thing. On the other hand, the nifty pointer **IS** the standard solution for this; it's used in almost all implementations of the standard library, and it works (with limitations). And I've yet to see any alternative which works. – James Kanze Feb 06 '12 at 19:14
  • @JamesKanze: I will agree that it is an idium (though in my opinion obsolete) to fix this specific problem. **BUT** only because the OP said they could not use the preferred `standard idiom` of using a function with a static member. Which solves this problem in a much cleaner way. As I have pointed out several time now the standard is forced to use it because of backward compatibility issues (std::cout is and must remain an object). – Martin York Feb 06 '12 at 19:23
  • I totally agree. Were `std::cout` being defined today, it would be `std::cout()`. At least, I hope---`cout` is a bit special, because there is also an action to carry out when the last destructor of `ios_base::Init` is called. Also: the more I think about it, the more I like your solution using a reference in anonymous namespace. It is a lot simpler, enough so to justify breaking with the established idiom (but only if you have to implement a legacy interface, and can't just use the function). – James Kanze Feb 07 '12 at 08:26
0

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; }
Martin York
  • 257,169
  • 86
  • 333
  • 562