Jump to content
  • Advertisement
Sign in to follow this  
Castaa

C++ in Linux: global object initialization hell!

This topic is 3559 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I recently upgraded my development system to Ubuntu 8.04. Which has the upgraded version of gcc/g++ compiler compared to Ubuntu 7.10. Even though it's the same project, my global variables/objects are being initialized in a different order than they were with the old compiler. This is causing a crash before main() because there is a dependency between two globals. I can't move the global declaration and initialization these because this breaks the application when built using the older g++ version which other devs still use. I'm reading that the only way to achieve predictable global initialization order is to have all globals declared in the same file. This is painful in my case. In Linux, is there a way to force global order another way or to create and call a function before anything including main()? Or other advice from the minds here?

Share this post


Link to post
Share on other sites
Advertisement
Don't do it?
Why can't you just have an Initialize function that you call at the top of main. That solves all the problems. That function can take care of setting up your globals in any order that they need to be setup in.
Any global variable that has an initialization that can decay to said global existing in the data segment of the executable will be safe, but you shouldn't be trying to depend on functions (constructors) being called in any predictable manner before main.

Also, a hint from the C++ Faq Lite, you could just create "init on first use" semantics for said objects. That way you can be assured that they are created before you use them (though this incurs run time overhead every time you call said object which may not be acceptable in tight loops and the like)

Share this post


Link to post
Share on other sites
Bear in mind this is not just an issue affecting switching OS or even compliers. Since it is undefined between different translation units, even an update to an existing compiler could technically break this behaviour.

As KulSeran points out, relying on this order is relying on undefined behaviour.

Obviously though the probem with using an Initialise function for anything other than simple types means that you can no longer initialise via construction and your objects may therefore need to be capable of existing in an uninitialised state.

This is yet another good reason to try to avoid globals.

However, if you are stuck with an old codebase and it is impractical to thoroughly redesign, an Initialise() method is probably your best bet.

Share this post


Link to post
Share on other sites
Globals arnt that bad (they just arnt fassionable currently.)
You can solve the init order though by fixing them in a function.



//someClass g_foo; //was
someClass & g_foo()
{
static someClass s_foo;
return s_foo;
}


Then all you need to do is search an replace all of your refs to g_foo -> g_foo()

Warning: May not help with shut down order. Your dependences still may bite there. But this has worked for me under quite a few compilers.

The other thing I have done to fix this (required for a DLL issue), was to alloc static buffer the same size as my class, and inplace new them.

This method gives you full control over you (de)construction whil still leaving the memory in global space.

Hope this is helpful.

Share this post


Link to post
Share on other sites
It is not a question of fashion - it is a question of experienced designers working on larger and more complex projects and learning what causes problems - problems much like the one posted by the OP.

I'd agree that the Meyers Singleton you suggest is another post-problem-fix when stuck with an old codebase, but surely you'd agree that it would have been better to design in the first place to avoid the issue altogether?

The lack of control of order of destruction that the Meyers pattern above gives you can actually be far more of a problem in some cases that you seem to acknowledge above - as far as I am aware, the order of destruction of statics across translation unit boundaries is as undefined as the order of construction of plain globals, and unlike a more traditional singleton pattern, the Meyers does not give you the option to destruct manually.

Order of initialisation issues is just one of the many problems that globals can cause - others relate to code-maintenance, modularity, concurrency, debugging etc etc.

Share this post


Link to post
Share on other sites
Quote:
Original post by Castaa

I'm reading that the only way to achieve predictable global initialization order is to have all globals declared in the same file.


Yes, C++ does not define the order of initialization.

Quote:
In Linux, is there a way to force global order another way or to create and call a function before anything including main()?


Probably, but it has nothing to do with either Linux or C++, since neither of those is aware of these problems. You may be able to specify the order while linking, but that is fragile as well.

Quote:
Or other advice from the minds here?


Don't use globals? Their initialization is by definition undefined, so there really is no easy way out.

Quote:
Globals arnt that bad (they just arnt fassionable currently.)


Isn't this somewhat of a counter-claim to the very existence of this thread? Mutable globals are bad, especially in C++.

Share this post


Link to post
Share on other sites
Currently you are relying on undefined behaviour of object initialisation and you should try and remove this as people in this thread have pointed out. However in regard to your question "In Linux, is there a way to force global order another way..." Yes you want to use init_priority, here is an example.

edit: ninja'd

Share this post


Link to post
Share on other sites
I am using this singleton class setup for almost any class that needs to be used in a lot of places and that I need only one instance for (examples: Window, TextureLibrary, GlslLibrary, SoundLibrary, etc.):



class Window
{
static Window *s_instance;

public:
static Window &initialize();
static Window &instance();
static void destroy();

private:
Window();
Window(Window const &other); // not implemented: you should not be able to copy a singleton
~Window();
};

inline Window &Window::initialize()
{
if (s_instance)
throw string("Window was already initialized");

return *(s_instance = new Window);
}

inline Window &Window::instance()
{
if (!s_instance)
throw string("Window was not yet initialized");

return *s_instance;
}

inline void Window::destroy()
{
delete s_instance;
s_instance = 0;
}

inline Window::Window()
{
// your constructor
}

inline Window::~Window()
{
// your destructor
}




This works perfectly for me, no need for global variables this way and I am still able to destruct the Window instance in the way I want. Example usage:


int main(int argc, char **argv)
{
try
{
Window::initialize();
Game::initialize();
}
catch (string const &error)
{
// a fatal error occured
cerr << "Error: `" << error << "'." << endl;
return 1;
}

// game is also a singleton
Game &game = Game::instance();

// run your game!
game.run();

// when finished:
Game::destroy();
Window::destroy();

return 0;
}


Share this post


Link to post
Share on other sites
Quote:
Original post by TheFlyingDutchman
I am using this singleton class setup for almost any class [...] that I need only one instance for

NEEDING one instance and NOT ALLOWING MORE THAN one instance is not the same. You are abusing the singleton pattern.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!