Sign in to follow this  
Castaa

C++ in Linux: global object initialization hell!

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
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
Quote:
Original post by TheFlyingDutchman

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:

*** Source Snippet Removed ***


int main(int argc, char **argv)
{
try
{
Window window;
Game game(window);
game.run();
}
catch (std::exception & e)
{
cerr << "Error: `" << e.what() << "'." << endl;
return 1;
}
return 0;
}

No cleanup needed, systems are clearly inter-connected during construction, and you're guaranteed proper de-initialization order.

Your approach is fine - but it's idiomatic for C. C++ offers implicit construction/destruction. The above style does exactly the same, yet if applied throughout the project, results in simpler and more robust code, that does not suffer from undefined behavior.

And most importantly, every C++ class has initialize(), destroy() and instance() functions already. They're constructor, destructor and instance respectively.

Allocating something statically brings along many issues which are very rarely required.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Quote:
Original post by TheFlyingDutchman

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:

*** Source Snippet Removed ***


int main(int argc, char **argv)
{
try
{
Window window;
Game game(window);
game.run();
}
catch (std::exception & e)
{
cerr << "Error: `" << e.what() << "'." << endl;
return 1;
}
return 0;
}

No cleanup needed, systems are clearly inter-connected during construction, and you're guaranteed proper de-initialization order.

Your approach is fine - but it's idiomatic for C. C++ offers implicit construction/destruction. The above style does exactly the same, yet if applied throughout the project, results in simpler and more robust code, that does not suffer from undefined behavior.

And most importantly, every C++ class has initialize(), destroy() and instance() functions already. They're constructor, destructor and instance respectively.

Allocating something statically brings along many issues which are very rarely required.


We were talking about singletons and their purpose is to be single. So no multiple instances of a singleton class are allowed. Therefore you make the constructor, copy constructor and destructor (and probably also operator=) private. This way the user can only obtain a reference to the instance and he can not accidentally delete the instance (what would happen, if in another location in the code you rely on that instance already being initialized, while you accidentally deleted it in another place? That should not be allowed, therefore the destructor is private).

So with this setup, you don't allow the user to create more instances of the class. He gets the advantage that the instance is globally available. The disadvantage is that you don't have implicit destruction. Therefore the static destroy member is added.

Quote:
Original post by DevFred
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.

Well, I think the examples I gave are pretty good examples of classes you only want one instance: Window, Game, Libraries. Typical classes that need only one instance (and why allow more of them? If my design is such that there is a single GlslLibrary, then my design should force that there is only one such library).

Share this post


Link to post
Share on other sites
I suspected this would turn into another singleton thread after TheFlyingDutchman's post, might as well close it now :)

Share this post


Link to post
Share on other sites
Quote:
Original post by dmail
I suspected this would turn into another singleton thread after TheFlyingDutchman's post, might as well close it now :)


Ah, sorry about that. I wasn't aware that singletons are more often discussed (perhaps even debated on) in this forum. I'll shut up from now on, I promise ;). Anyway, the topic starter now knows about singletons (maybe he did already before this topic) and if he thinks it is useful, he can use them. No need to discuss them further I guess.

Share this post


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

Well, I think the examples I gave are pretty good examples of classes you only want one instance: Window, Game, Libraries. Typical classes that need only one instance (and why allow more of them? If my design is such that there is a single GlslLibrary, then my design should force that there is only one such library).


Window - There is nothing anywhere that would in any way under any system mandate there may only ever be one. Often, there is no need for more than one, but claiming there can never be one is simply redundant.

There's games that run on 3 monitors. I believe Doom supported that in one of later patches. Flight simulator runs on arbitrary number of monitors.

Window is definitely not suitable for that.

Game is a binder class. The singleton advice is terrible if discussed in scope of Java. If I want a trivial multi-player application server (doesn't need to be networked), I will definitely want more than one Game. I'll want a game per player. Again, there is niche area where Game may be global (emphasis *may*). There's no reason where it should be *must*.

GlslLibrary - Does that happen to use C API that's defined over static context? If so, it can be wrapped well into RAII to encapsulate required functionality, yet retain the benefit of C++. If library exists in some static context, then it needs to be static. It may even be global (but doesn't need to be).

Everybody *must* die. Everybody *may* eat. And just because not eating causes one to die, doesn't mean they shouldn't bother.

Share this post


Link to post
Share on other sites
For what it's worth, if you have global pointers instead of global objects, you can initialize them whenever and however you want in main(), or some function called more or less directly by it. Problem solved, except for the problem of having globals at all.

Maybeyou could just re-factor your globals not to have dependencies on each other?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this