I'm using init_seg to control the creation of three C++ class objects. Each object is in a different source file/translation unit. Debugging shows the objects are being created as expected during CRT initialization.
The objects are being initialized in alphabetical order of their source file. I'd like to change it because its not quite right. I visited MSDN's page on init_seg, and it states the use is:
#pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} )
It appears the use of lib and section-name are mutually exclusive, so its not clear to me how to use init_seg(lib) and provide a section/group name to get the alphabetical ordering right.
When I attempt to use an alphabetized string to control order:
#pragma init_seg(lib, "01")
It results in a warning, which I am guessing means things are not going to work as expected:
warning C4081: expected ')'; found ','
When I try to insert directly into the CRT startup code directly using ".CRT$XCB", ".CRT$001", and ".CRT$XCB001" (and other variations of utilizing alphabetization):
#pragma init_seg(".CRT$XCB")
It results in another warning, which I am guessing means things are not going to work as expected:
warning C4075: initializers put in unrecognized initialization area
I found one question on Stack Overflow about it, but the answer was a guess and it does not cover multiple translation units. I also found an archive of KB104248 on the Wayback Machine, but its not much help either because it only shows the use of compiler, lib and user.
So my question is, how do I utilize init_seg to control the precise order of creation of my three objects in three different source files?
Here's what I found through testing on XP and VS2002/VS2003, Vista and VS2005/VS2008, Windows 7 and VS2008/VS2010, Windows 8 and VS2010/VS2012/VS2013, and Windows 10 using VS2015.
#pragma_init(<name>)has been available since VC++ 1.0 days. MS does not publish too much information about it, but we know its documented from VC++1.0 (archived KB104248) through VS2017.#pragma init_seg(lib)is almost perfect. However, the object files are alphabetized in VS2008 and earlier, so the init order isa-b-c(unwanted) rather thanc-b-a(desired). Its OK on VS2010 and above. What's not obvious is the order is laid out precisely asc-b-ain thevcprojfiles.#pragma init_seg(".CRT$XCB-0NN")seemed to work. Ourstd::stringsSTRING_AandSTRING_Bwere created early (and the objects were in the right order), butSTRING_Bcaused a crash at suhutdown. The address was0x0000000d, and it appears thestd::string(and its vtable) were destroyed too early.#pragma init_seg(".CRT$XCU-0NN")worked as expected during startup and shutdown. If I parsed what I read correctly, then theUin group nameXCUindicates user defined objects. It means our objects were created somewhere in-between what#pragma init_seg(lib)and#pragma init_seg(user)provides.So here is how to initialize an object C, then an object B, then an object A from source files
a.cpp,b.cppandc.cpp.Source file
a.cpp:Source file
b.cpp:Source file
c.cpp:Our use case was to create three read only objects and avoid the problems with C++'s static initialization order fiasco and Microsoft's thread local storage.
The technique avoids missing C++ dynamic initializers in C++03. It also side steps Microsoft's inability to provide C++11's Dynamic Initialization and Destruction with Concurrency (or more correctly, Microsoft's failure to provide the core language feature for 10 years).
Here's a reference to the issue on Thread Local Storage (TLS) on MSDN:
Its also worth mentioning that there does not appear to be a limit on the number of characters in the section or group name. The archived KB 104248 uses the name
"user_defined_segment_name"with 26 characters.