Game initialization.

Started by
9 comments, last by irreversible 4 years, 10 months ago

I've reading about game/engine initialization both in Game Engine Architecture and Game Coding Complete. I have understood there is a problem with C++ and initialization, because initialization of global variables is non-deterministic, and the order your initialize modules of engine is critic since some modules depends other modules. They explains some approaches to avoid the undefined behavior, but I wonder something. Why must I initialize modules like global variables? Why don't initialize like members of Application object? Maybe last would lead to annoying scope issues, but, why don't initialize in main() scope? Would they have "global" scope in main() and deterministic initialization?

Thank you and sorry for my English.

 

Advertisement

There's no good reason to not initialize things from main directly. Typically people try to build complicated systems that rely on running code from constructors inside of global or static objects. After trying a few out and writing some myself I personally decided they are unnecessary and not worth the trouble.

In my own code I just call functions from main in a deterministic and simple way.

The main reason for initializing things outside of main is that they are needed for other things that also exist outside of main.  This sounds like a circular argument, but consider that every static variable has a lifetime that extends beyond main.  If one of your functions has a static variable of type T, and T accesses the logging system in its destructor, then the logging system and its dependencies need to be available outside of main.

You can, of course, avoid this whole issue by not using any static variables.  However, this requires a lot of discipline, and it has costs both in code complexity and performance.

Using static variables doesn't imply that they must have a lifetime longer than main.

12 hours ago, midn said:

Using static variables doesn't imply that they must have a lifetime longer than main.

Um, yes it does.  In C++, static variables guaranteed to survive past the end of main.  That is how the language works, and there's nothing you can do about it.

You might be thinking about cases where you set the static variable to some harmless dummy value, such as nullptr for pointers, at the end of main.  You can of course do that, but that doesn't change the essential fact that the variable itself has a lifetime that extends beyond main.  It also requires knowing where all of your static variables are, which can break encapsulation and/or require writing extra boiler-plate code, especially for function-static variables.

Correct me if i am wrong because i am not an expert, but this is only the case if they where actually constructed in a specific run, right ? I mean, it may be the case that a piece of code declaring a static variable (edit: outside of main) might not be executed because it was never reached. But if it was executed, then the static stuff will live until the program terminates.

Correct ?

Yes, static variables that are never constructed are also never deconstructed - but in that case, the variable doesn't really "exist" (i.e. have a lifetime) at all, so my short statement is still basically correct.  The rules for determining which static variables actually get constructed, in which order and at which point in the program, are actually quite complicated with lots of special cases to consider.

Static variables are necessary at least at the point of singletons, fixed size buffers and one-time startup information you don't want to calculate every time again. Examples are a log or profiling buffer or UTC time. Whyt I usually do is wrapping the engine entry point into a macro calling some start-/ and cleanup code arround the user main.

What you generaly want is also reading some kind of config destinated to your game from a file in the game directory like the "engine.ini" file to set specific configuration to your engine or have a "userprefs.ini" that set local user settings like display resolution

There are some subtleties here.

Quote

I have understood there is a problem with C++ and initialization, because initialization of global variables is non-deterministic

Global variables are not all static variables and vice versa. It is perfectly possible to have a static variable in a function that gets initialized upon first usage (this is more complicated than you'd think, especially with multi-threading in mind), which then is not a global. I think this is what user "midn" refers to:

Quote

Using static variables doesn't imply that they must have a lifetime longer than main

That said static globals do imply that lifetime is longer than main as per definition they get 0 initialized when the executable is loaded. They usually live in their own segments (.bss or something like that) To try to answer your question more directly:

Quote

Why must I initialize modules like global variables? Why don't initialize like members of Application object?

What you are saying is a perfectly viable solution. However as your program grows in size you will run into problems where the application object will have a lot of references to other things. If you were to use 3rd party libraries, then there would be no way for your application object to 'own' those references. 

To that end people have tried to use global variables (non static as static linkage hides the symbol for external linkage) and then you get the initialization order problem. If you have two variables A and B, and you know A needs to be initialized before B, then easy. But if you have 100+ variables, then it essentially becomes a graph. 

And this is generally where people reach for the singleton pattern to 'solve' their problems https://en.wikipedia.org/wiki/Singleton_pattern. (Side note for C++ is that using pointer statics and using new/malloc for initialization in the singleton will cause memory leaks upon shutdown. Which isn't a problem if the process terminates, but you should probably register an atexit function to free the memory if you were to use CRT debugging facilities to track memory leaks: https://docs.microsoft.com/en-us/visualstudio/debugger/finding-memory-leaks-using-the-crt-library?view=vs-2019. I mention this because it is often overlooked.)

The problem however with singleton patters is that if the singleton itself depends on another resource then you create a dependency chain. It'll work, but the main objection is lack of control of order of initialization. E.g. if you have a RenderSystem singleton that needs to load some files using a FileSystem singleton, then when you were to call RenderSystem::GestInstance() it would have to instantiate the FileSystem singleton. This in itself is fine, but becomes problematic if you want to control the order. The order is usually controlled because some system needs to be configured. For example the FileSystem could set a file search directory based on which map is loaded, etc. And because you can't set those settings before main runs (well you can with a custom linker entry point, but that's a whole other story) you are now back to the original problem or initialization order.

To add to this problem is that C++ doesn't have a very portable way of forcing initialization order. Clang/GNU/MSVC all have their own unique ways of controlling static initialization order. So how to solve this?

Well the most common method is to not use lazily initialized singletons, but rather in main call say


FileSystem::CreateInstance(settings)
RenderSystem::CreateInstance(settings)

// Now do actual things

But now you essentially might as well have used global variables. Which is what you're saying:

On 5/24/2019 at 2:17 PM, Goyira said:

why don't initialize in main() scope? Would they have "global" scope in main() and deterministic initialization?

And this is generally what happens... globals initialized in 'main' (or some other top level function) for things where the order matters. And lazily initialized singletons for things where the order does not matter.

This topic is closed to new replies.

Advertisement