The difference between a good programmer and a mediocre one is the good programmer has seen more errors and fixed more problems than the mediocre one. With this in mind, I’m going to start a segment on my blog where in I will share some of the little things I’ve picked up in hopes it will save someone out there some time. I haven’t come up with a clever title for this yet, but when I do, I’ll let you know.
So let’s get started! Today, I’d like to share with you a little trick I picked up from David Eberly’s books. This tidbit deals with pre-main static initialization in C++.
The Problem:
When you create several static variables or class members in C++, there is no guarantee as to the order in which C++ will initialize their values. Let’s say you have a vector class. You’ve defined a static member somewhere in your vector class called UNIT_X, which is the unit vector along the X axis. Somewhere else in your code, you want to initialize another static vector called sMyUnitX to UNIT_X. This doesn’t present a problem if UNIT_X is initialized first, but because there is no way of knowing which static variable C++ will initialize first, there could arise a situation where the sMyUnitX is initialized before UNIT_X, in which case, sMyUnitX will be filled with zeros instead of the unit vector like it should be.
Solution:
The solution is to somehow defer the initialization of sMyUnitX until after we’ve entered main, thus insuring UNIT_X has been initialized before we assign its value to sMyUnitX. To accomplish this, we’ll create a class called Main. Main will have two static methods: Main::Initialize() and Main::Terminate(), and will store an array of initializer and terminator function pointers. Here’s the class:
(Note: WordPress is dumb, and won’t let me put less than or greater than signs in my code without thinking it’s another tag. Those two Array variables down there are supposed to be templates of type Initializer and Terminator)
(Another note: Would somebody please tell me how to get WordPress to retain code indentions?)
// We typedef the initializer and terminator function pointers
typedef void (*Initializer)(void);
typedef void (*Terminator)(void);
class Main {
public:
static void AddInitializer(Initializer initializer);
static void AddTerminator(Terminator terminator);
static void Initialize();
static void Terminate();
private:
static Array<initializer> sInitializers;
static Array<terminator> sTerminators;
}
Main holds two arrays of function pointers. All of the initializers are called when Main::Initialize() is called, and all of the terminator functions are called when Main::Terminate() is called. Here’s what your main function should look like:
void main() {
Main::Initialize();
// Do something cool
Main::Terminate();
}
This is cool and all, but how do we set the initializers and terminators in the first place? That obviously must be done pre-main. There is no way to explicitly tell C++ to execute code pre-main. All C++ will do is initialize static variables. We can however include a function call as part of a static variable’s initialization, thus forcing C++ to call a function pre-main.
// SomeFunction returns a bool, and because it's part
// of sMyStaticVar's initialization, it is called pre-main.
bool MyClass::sMyStaticVar = SomeFunction();
We’ll exploit this little trick and build three preprocessor macros for registering initializers and terminators with Main. Here are the macros for registering initializers:
#define DECLARE_INITIALIZER \
public: \
static bool RegisterInitializer(); \
static void Initialize(); \
private: \
static bool sIsInitializerRegistered \
#define IMPLEMENT_INITIALIZER(classname) \
bool classname::sIsInitializerRegistered = 0; \
bool classname::RegisterInitializer() { \
if (!sIsInitializerRegistered) { \
Main::AddInitializer(classname::Initialize); \
sIsInitializerRegistered = 1; \
} \
return sIsInitializerRegistered; \
}
#define REGISTER_INITIALIZER(classname) \
static bool sIsInitializerRegistered_##classname = classname::RegisterInitializer();
The DECLARE_INITIALIZER macro goes in your class definition somewhere, and sets up the function Initialize(), in which you will put any static initialization you need to defer to post-main, the function RegisterInitialize() which is responsible for adding a pointer to Initialize to Main, and a bool to make sure we don’t initialize twice. The IMPLEMENT_INITIALIZER macro implements the RegisterInitialize() function, and should go in your .cpp file somewhere. The REGISTER_INITIALIZER macro goes in your .h file outside of your class definition, and by using the little pre-main function call trick discussed earlier, actually called the RegisterInitializer() method.
For clarification, here is where the macros go:
// In your .h file:
#include "Main.h"
class AwesomeClass {
DECLARE_INITIALIZER;
public:
AwesomeClass();
~AwesomeClass();
// More methods...
};
REGISTER_INITIALIZER(AwesomeClass);
// In your .cpp file
IMPLEMENT_INITIALIZER(AwesomeClass);
void AwesomeClass::Initialize() {
// Write your initialization code here.
}
That’s it for now!