Problems with static initialization
I am having some strange static initialization issues. I am using a code generator to generate structs and serialization code for a messaging system I wrote. To be able to easily distribute a message based on this post ID, I have a code generator so that for each post type there is something similar to the following:
MessageAllocator s_InputPushUserControllerMessageAlloc(INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID, (AllocateMessageFunc)Create_InputPushUserControllerMessage);
The MessageAllocator class basically looks like this:
MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
if (!s_map) s_map = new std::map<uint32_t, AllocateMessageFunc>();
if (s_map->insert(std::make_pair(messageTypeID, func)).second == false)
{
//duplicate key!
ASSERT(false, L"Nooooo!");
}
s_count++;
}
MessageAllocator::~MessageAllocator()
{
s_count--;
if (s_count == 0) delete s_map;
}
where s _
map and s _
count are static members of MessageAllocator. This works most of the time, but sometimes messages are not added to the map. For example, this particular message is not added unless I call Create _
InputPushUserControllerMessage () somewhere in my startup code, however the other messages work fine. I thought it might have something to do with the linker wrongly thinking that the type is not showing up and deleting it, so I disabled it with the / OPT: NOREF switch (I'm using Visual Studio 2008 SP1), but that had no effect.
I know the problem of "static initialization order fiasco", but as far as I know, the order in which these objects are created should not change the result, so it seems fine to me.
Any insight here would be appreciated.
a source to share
It turns out that object files containing static initializers were not included by the linker because nothing referenced any functions in them. To get around this, I used "C" - one of the generated functions to have a predictable non-malformed name, and then forcibly reference it using a pragma like this for each post
#pragma comment(linker, "/include:Create_GraphicsDynamicMeshCreationMessage")
which I put in a generated header file which is later included in all other non-generated files. This is MSVC only a kind of hack, but I guess I can do something similar on GCC once I eventually port it.
a source to share
Put the static in the class so it's a static member of the class
struct InputPushUserControllerMessageAlloc { static MessageAllocator s_obj; };
MessageAllocator InputPushUserControllerMessageAlloc::s_obj(
INPUT_PUSH_USER_CONTROLLER_MESSAGE_ID,
(AllocateMessageFunc)Create_InputPushUserControllerMessage);
The standard allows it to delay the initialization of namespace scoped objects until some function / object from its translation unit has been used. If initialization has a side effect, it cannot be optimized. But this does not prevent it from being postponed.
Not so for objects that have a class. So it might prohibit optimization there.
a source to share
I would change s_map from a static class member to a static method member:
std::map<uint32_t,AllocateMessageFunc>& MessageAllocator::getMap()
{
// Initialized on first use and destroyed correctly on program termination.
static std::map<uint32_t,AllocateMessageFunc> s_map;
return s_map;
}
MessageAllocator::MessageAllocator( uint32_t messageTypeID, AllocateMessageFunc func )
{
if (getMap().insert(std::make_pair(messageTypeID, func)).second == false)
{
//duplicate key!
ASSERT(false, L"Nooooo!");
}
}
No destructor or counter needed.
If your globals are in separate DLLs (or shared libraries) that are lazy loaded. This may cause a problem similar to your description.
a source to share