ggambetta
ggambetta

Reputation: 3433

Static variables not being initialized - sometimes?

I have a game with puzzles. Puzzles are created from script files using a puzzle engine ID. Puzzle engines register themselves using a static variable in a static hash. I'm having reports of crashes and we've tracked down the problem to the hash not having certain keys, which should have been added by the static functions.

Note that this is a bug we can't reproduce, that is, the method works in general. In fact I have no idea why sometimes it apparently doesn't. Is there any possibility of these static functions not being called? Maybe a compiler bug? (Visual C++ 2005)

The puzzle factory type

class PuzzleMode;
typedef PuzzleMode* (*fnPuzzleFactory)(bool* bVictory, const char* sPuzzleCode, const GFC::StringList& lParams);

Engines use this macro to make sure they get registered. There is a static bool which is initialized as the result of a function that registers the factory and returns true :

#define REGISTER_PUZZLE(CODE, CLASSNAME)                                                                    \
    PuzzleMode* puzzleFactory##CLASSNAME (bool* bVictory, const char* sCode, const StringList& lParams)     \
    {                                                                                                       \
        return new CLASSNAME(bVictory, sCode, lParams);                                                     \
    }                                                                                                       \
                                                                                                            \
    static bool registerPuzzle##CLASSNAME (void)                                                            \
    {                                                                                                       \
        PuzzleMode::registerPuzzleFactory(CODE, puzzleFactory##CLASSNAME);                                  \
        return true;                                                                                        \
    }                                                                                                       \
                                                                                                            \
    static bool s_bRegisteredPuzzle##CLASSNAME = registerPuzzle##CLASSNAME();

Example, in the puzzle engine .cpp :

REGISTER_PUZZLE("bloodcollection", BloodCollection);

Somewhere else, in a .cpp :

static Hash<String, fnPuzzleFactory>* s_hPuzzleFactories = NULL;

void PuzzleMode::registerPuzzleFactory (const char* sId, fnPuzzleFactory pFactory)
{
    if (!s_hPuzzleFactories)
        s_hPuzzleFactories = new Hash<String, fnPuzzleFactory>();   

    String sLowId = String(sId).lower();
    s_hPuzzleFactories->setAt(sLowId, pFactory);
}

All of that is supposed to happen when the app starts. Much later it's used like this

PuzzleMode* PuzzleMode::createPuzzleInstance (const char* sEngine, bool* bVictory, const GFC::StringList& lParams)
{
    // Shouldn't happen
    if (!s_hPuzzleFactories)
        s_hPuzzleFactories = new Hash<String, fnPuzzleFactory>();

    String sLowId = String(sEngine).lower();
        if (!s_hPuzzleFactories->hasKey(sLowId))    // <----- HERE
        return NULL;

    fnPuzzleFactory fnFactory = s_hPuzzleFactories->getAt(sLowId);
    return fnFactory(bVictory, sEngine, lParams);
}

And sometimes, but only sometimes, in the line marked above, the hash doesn't have the key.

Any ideas? The only thing I can think of (and I thought of it while writing this) is that the register function is called before the hash itself is initialized to NULL, but everything should crash and burn way before we find the hash doesn't have the key we're looking for.

Upvotes: 2

Views: 1986

Answers (3)

ggambetta
ggambetta

Reputation: 3433

Looks like this had nothing to do with static initialization after all - after a lot of logging we found out some unrelated code was destroying the pointer, apparently.

Upvotes: 0

Carl Seleborg
Carl Seleborg

Reputation: 13295

The linker is allowed to drop a compilation unit completely if it thinks that it contains no reachable code. So if none of the functions in your .cpp file is explicitly called from elsewhere, it may get discarded entirely, and the object that should have been registered is simply missing from your executable.

Try to put a dummy external function in the .cpp of the missing puzzle and call it from your main() to see if it solves your problem.

Upvotes: 1

Joris Timmermans
Joris Timmermans

Reputation: 10978

This is probably a static initialization order issue. Try rewriting it as "construct on first use".

Upvotes: 2

Related Questions