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.