Fixing C++ static and global variable initialization
Order of static and global variables initialization is a well-known uncontrolled phenomena. There is no way you can change it, variables will be initialized in an order the compiler is the sole to decide. The singleton fixes this problem by postponing the initialization at the first time the object is used. But then creation time is unknown and multi-threading issues may appear. This article propose a simple technique to force order of initialization.
Let’s recall the C++ rule : Static member variables are initialized at last when the first instance of the class is created or when the first call to a static method is made. It also says that all globals are initialized before entering main function. Our technique will use those properties to order initialization.
The idea is pretty simple : create a list of initialization function, sort it and call all functions in order. While simple, there is some problems to solve.
How to create this list? Simply by using a global helper variable. Another C++’s rule states that a global variable should not be stripped if its constructor has side effect. Interesting! So there is the code that we use
struct STATIC_DATA_FUNCTION_DECLARATOR { STATIC_DATA_FUNCTION_DECLARATOR( STATIC_DATA_ENTRY * entry, const STATIC_DATA_FUNCTION function, const char * name, const char * predecessor_table, const char * successor_table, const STATIC_DATA_FUNCTION_TYPE type ) { STATIC_DATA::AddFunction( entry, function, name, predecessor_table, successor_table, type ); } };
Then just create a global variable of this type :
void initialize_function(); STATIC_DATA_FUNCTION_DECLARATOR function_declarator( entry, &initialize_function, "ClassX", "ClassY", "ClassZ", STATIC_DATA_FUNCTION_TYPE_Initializer );
While the previous class helps us creating the list, it is a mess to use. You have to write a method, then declare the global helper object. The result is an ugly code that nobody wants to remember. Usually, when I get there, I think it’s time for our friends macros to take care of that.
STATIC_DATA_InitializeType( TypeX, "TypeY", "TypeZ" ) { // Initialization code here } STATIC_DATA_FinalizeType( TypeX, "", "" ) { // Finalization code here }
This code is preprocessed to :
void TypeX_InitializeType( void ); STATIC_DATA_FUNCTION_DECLARATOR TypeX_Initializer( &STATIC_DATA_HELPER<TypeX>::Entry, &TypeX_InitializeType, "TypeX", "TypeY", "TypeZ", STATIC_DATA_FUNCTION_TYPE_Initializer ); void TypeX_InitializeType( void ) { // Initialization code here } void TypeX_FinalizeType( void ); STATIC_DATA_FUNCTION_DECLARATOR TypeX_Finalizer( &STATIC_DATA_HELPER<TypeX>::Entry, &TypeX_FinalizeType, "TypeX", "","", STATIC_DATA_FUNCTION_TYPE_Finalizer ); void TypeX_FinalizeType( void ) { // Finalization code here }
There is another little trick there. To ensure there is only one node per type (the node stores both the initialization and finalization functions), a template class that just defines one static variable is used:
template < class _TYPE_ > struct STATIC_DATA_HELPER { static STATIC_DATA_ENTRY Entry; }; template< class _TYPE_ > STATIC_DATA_ENTRY STATIC_DATA_HELPER<_TYPE_>::Entry;
Inside STATIC_DATA::AddFunction, the entry is appended to the current list. So, if C++ rules are followed, when the program arrives in the main function, all the global helpers are initialized and the list must be complete. From there we call the function that will order the list and we call the initializer. Before exiting, we call the finalizers in reverse order.
int main( char* argument_table[], int argument_count ) { STATIC_DATA::CallInitializers(); // Some usefull code STATIC_DATA::CallFinalizers(); }
The ordering criteria is simply the text that is passed in the macro. Several types might be passed. The second text contains successors of the initialization method.
STATIC_DATA_InitializeClass( SCRIPT, "MEMORY FILESYSTEM", "CONFIGURATION_SCRIPT" ) { }
I’m not covering all aspects of the code, but you can find it with this working example. Enjoy!








