5

I'm compiling a shared library with two compilation units: globals.cpp and stuff.cpp. The globals.cpp file initializes a handful of extern variables that are used in stuff.cpp. The issue I'm experiencing is that the code in stuff.cpp is running before the code in globals.cpp has had the opportunity to assign a value to the extern variables. E.g., I'm seeing a bunch of 0 values being used. This issue depends on what platform I compile/run the code on -- some work and some do not.

How does one go about resolving this? Can I force globals.cpp to run first?

sholsapp
  • 15,542
  • 10
  • 50
  • 67

3 Answers3

6

You can't (in a consistent manner)

But you can work around it.

global.cpp

// If you have a global variable that has to be initial by a constructor
MyObj globalX; 

// Instead do this

MyObj& globalX() { static MyObj x; return x;}

You still have a global variable. But by putting it in a function we know when it is being used. By using a static member of the function it is initialized the first time the function is called but not after that. Thus you know that it will be correctly constructed before first use.

Martin York
  • 257,169
  • 86
  • 333
  • 562
2

From the comp.lang.c++ FAQ, see:

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • As usual the FAQ gives bad advice. Using pointers means the objects are not consistently destroyed and the subsequent point about the problems with order of destruction is just plain wrong: http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems#335746 – Martin York Feb 01 '12 at 08:06
  • @LokiAstari: Interesting. Is it completely wrong? The FAQ mentions "If the constructors of a, b and c use ans, you should normally be okay since the runtime system will, during static deinitialization, destruct ans after the last of those three objects is destructed.", although I agree that it shouldn't be so dismissive of it. – jamesdlin Feb 01 '12 at 12:25
  • He dismisses leaking as not a problem because memory is cleaned up by the OS. This is just **sloppy and BAD**. Anything with a constructor/destructor then its not really the memory; its about the resources these objects hold and their clean up. There could be anything in these objects and when you modify an object do you check all the places you `intentionally leak` to make sure the leaking is not affecting your codes correctness (Iam guessing you could not even find these places). So the only time his comment is relevant is for stuff with no constructor/destructor. – Martin York Feb 01 '12 at 20:47
  • (cont...) But for these objects you don't even need to use this technique as a global variable are guaranteed to be initialized before use: `int globaleX = 5;`. When there is no code to run for initialization you don't need this technique at all. Thus he is completely wrong. But I digress. Personally I find the site emphasis a lot of bad techniques and has several glaring errors that are not obvious for a beginner and the authors lack of willingness to correct or comment on these bad bits makes the site unreliable. This is also why SO prefer links to internal discussions rather external sites. – Martin York Feb 01 '12 at 20:54
  • As an easy to find example: Can you spot whats wrong here: http://www.parashift.com/c++-faq-lite/new/placement-new.html – Martin York Feb 01 '12 at 20:58
0

I'm assuming you are seeing this behavious because you are doing inline initialization of global variables without an explicit function call. e.g. globals.cpp:

 // top of source file

 #include "myincludes.h"

 CSomeClass someObject(432);
 int global_x = 42;
 int global_y = InitY();

The constructor and destructor order of global objects and global variable initialization order is mostly non-deterministic. (I would surmise, without consulting the standard reference pages, that variables in a source file get initialized from top declaration to bottom, but the order of "which source file comes first" is not defined.)

The best solution is to not have any global objects (where the constructor gets run before any functions in the library) or have a dependency on global variable initialization order.

Better to have a function that explicitly initializes your library. Perhaps you require the app to call this function one on startup or the exported functions of your library call it after detecting the initialization has not occurred. Initialize your globals then.

On my team, code that runs before "main" (or "DllMain") is strictly disallowed. In other words, no global objects. No functions to init globals.

selbie
  • 100,020
  • 15
  • 103
  • 173