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.

0


a source to share


4 answers


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.

0


a source


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.

+2


a source


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.

+1


a source


You will not return a null pointer.

MessageAllocator::~MessageAllocator()
{
    s_count--;
    if (s_count == 0) 
    {
          delete s_map;
          s_map = 0;
    }
}

      

0


a source







All Articles