tags:

views:

471

answers:

4

I'm having some weird issues with static initalization. I'm using a code generator to generate structs and serialization code for a message passing system I wrote. In order to have a way of easily allocating a message based on it's message id I have my code generator ouput something similar to the following for each message type:

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 other messages work fine. I thought this might be something to do with the linker incorrectly thinking the type is unreferenced and removing it so I disabled that using the /OPT:NOREF switch (I'm using Visual Studio 2008 SP1) but that had no effect.

I'm aware of the problem of the "static initialization order fiasco" but as far as I know the order in which these objects are created shouldn't alter the result so this seems ok to me.

Any insight here would be appreciated.

A: 

I would change s_map from a static class member into 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 need for destructor or a count.

If your global objects are in separate DLL's(or shared libs) that are lazy loaded. This may cause a problem similar to your description.

Martin York
That's a good tip, thanks. Although my problem still persists. To answer people's questions: yes it's in a DLL but the entire system is contained within the DLL. All the messages are defined in the DLL and all the code that uses the system is also in the DLL so I dont think DLLs loading or unloading could cause this.
Lucas
A: 

You are not setting the pointer back to null.

MessageAllocator::~MessageAllocator()
{
    s_count--;
    if (s_count == 0) 
    {
          delete s_map;
          s_map = 0;
    }
}
martsbradley
True, although I'm not sure how that's a problem since the destructors are only called when the DLL is unloaded
Lucas
+2  A: 

Put the static into a class so it is a static member of a 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 initialization of objects having namespace scope until any function/object from its translation unit is used. If the initialization has side-effect, it can't be optimized out. But that doesn't forbid delaying it.

Not so of objects having class-scope. So that might forbid it optimizing something there.

Johannes Schaub - litb
Wow, I never would have thought of that in a million years. Gotta love C++. Unfortunately, I tried your suggestion and I still have the same problem. :(
Lucas
Also: This would explain why using any function from that file made those messages appear
Lucas
It's explained in 3.6.2/3 in the C++ Standard, if you want to look it up.
Johannes Schaub - litb
Imagder, try to disable whole-program-optimization. Maybe it's going crazy
Johannes Schaub - litb
Thanks, I'll check it out. I'm building in Debug mode so it's already off but yeah I could see it messing that up.
Lucas
A: 

Turns out that the object files containing the static initializers were not included by the linker because nothing referenced any functions in them. To work around this I extern "C"-ed one of the generated functions so that it would have a predictable non-mangled name and then forced a reference to it using a pragma like this for each message

#pragma comment(linker, "/include:Create_GraphicsDynamicMeshCreationMessage")

which I put in the generated header file that is later included in all the other non-generated files. It's MSVC only and kind of hack but I assume I can do something similar on GCC once I eventually port it.

Lucas