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.