0

Doesn't a compiler have all the information it needs to generate a dependency tree of all globals and create a well defined and correct initialization order for them? I realize you could write a cyclic dependency with globals - make only that case undefined behavior - and the compiler could warn and maybe error about it.

Usually the reason for this sort of thing is that it would be burdensome to compiler makers or cause compilation to slow significantly. I have no metrics or evidence that indicates either of these wouldn't be true in this case, but my inclination is that neither would be true.

9
  • Well, "unspecified" doesn't preclude a compiler from specifying the order... I guess nobody has done it yet? Commented Sep 20, 2013 at 19:44
  • 1
    What about a global defined in one translation unit whose initialization depends on a global in another translation unit being initialized? You'd be imposing some sort of link-time optimization on all compilers. Commented Sep 20, 2013 at 19:46
  • @Praetorian What kind of crap compiler doesn't already do 'some sort of link-time optimization'? Commented Sep 20, 2013 at 19:48
  • The problem is that the linker will need to know/understand what static initialization needs to be done in which order. That could turn out to be very complex, and the linker is generally not very clever... Commented Sep 20, 2013 at 19:48
  • 1
    @Dave It's probably likely the mainstream compilers/linkers are smart enough to resolve initialization dependencies as you suggest; however, the standard itself needs to cater to the lowest common denominator. I'm sure there are dozens of compilers for embedded targets that have no idea how to look across object files to perform the most basic LTO. Commented Sep 20, 2013 at 19:58

3 Answers 3

5

Hm, imagine the following setup, which is perfectly valid C++, but tricky to analyze:

// TU #1

bool c = coin();

// TU #2

extern bool c;
extern int b;
int a = c ? b : 10;

// TU #3

extern bool c;
extern int a;
int b = c ? 20 : a;

It is clear that TU #1 needs to be initialized first, but then what? The standard solution with references-to-statics allows you to write this code correctly with standard C++, but solving this by fixing the global initialization order seems tricky.

Sign up to request clarification or add additional context in comments.

3 Comments

By "seems tricky" I probably mean that the compiler would need to be able to generate arbitrary meta code, and possibly solve the halting problem.
Lol... that's a funny case. Stupid ternary. The initialization order would have to change depending on the value of c, which might not be compile time resolved (correct?). So, the only way to solve that is to allow the initialization order to change in run time depending on something in run time? Ya I doubt that'll ever happen.
@Dave: Well, it's hardly the fault of the conditional operator. You could have written any kind of equivalently troublesome code. This was just the shortest example I could come up with.
3

The part the compiler can deal with is actually define: objects with static storage duration are constructed in the order their definition appears in the translation unit. The destruction order is just the reverse.

When it comes to ordering objects between translation units, the dependency group for objects is typically not explicitly represented. However, even if the dependencies were explicitly represnted, they wouldn't actually help much: on small projects the dependencies between objects with static storage duration can be managed relatively easy. Where things become interesting are large objects but these have a much higher chance to include initializations of the form

static T global = functionWhichMayuseTheword();

i.e., in the case where the ordering would be useful it is bound not to work.

There is a trivial way to make sure objects are constructed in time which is even thread-safe in C++ (it wasn't thread-safe in C++03 as this standard didn't mention any concept of threads in the first place): Use a function local static object and return a reference to it. The objects will be constructed upon demand but if there are dependencies between them this is generally acceptable:

static T& global() {
    static rc = someInitialization();
    return rc;
}

Given that there is a simple work-around and neither a proposal nor a working implementation demonstrating that the proposal does work, there is little interest to change the state of how global objects are initialized. Not to mention that improving the support for global objects seems as useful as making goto better.

Comments

2

I am not a compiler author so take what I say with a grain of salt. I think the reasons are as follows.

1) Desire the preserve the C model of separate compilation. Link time analysis is certainly allowed, but I suspect they did not want to make it required.

2) Meyers Singleton (especially now that it has been made thread-safe) provides a good enough alternative in that it is almost as easy to use as a global variable but provides the guarantees you are looking for.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.