## The C++ Static Constructor "Registration" Trick In C++, static objects outside functions are supposed to have been constructed before main() starts, so that they are already available at that point. As such, the constructors for those objects do run during program initialization before main() is called, but in an unspecified order that makes it impossible to safely construct nontrivial objects. Many C++ style guides disallow static initialization of complex objects like this. For example, the Google C++ style guide contains this rule: Objects with static storage duration are forbidden unless they are trivially destructible. Here "trivially destructible" means "their destructor does nothing, not even run another destructor", and the underlying reasoning for that is that constructors and destructors of objects with static lifetime are not ordered between translation units (=~ .o files). What that implies is that, for example, the destructors for all the static objects in libc++ could run before the destructors for all the static objects in your library, which would probably result in unpleasantness if you tried to use the heap or stdio. However, there is still quite a lot of useful stuff one can do with static objects even under this stricture. One pattern one can apply here is the "static registration" pattern. ## Background In any C program of real size, there is a function named something like "init()", whose body looks like this: init_base(); /* defined in /base/init.c */ init_ipc(); /* defined in /ipc/init.c */ init_ui(); /* defined in /ui/init.c */ and whenever one wishes to add a new initializer one changes init() to call it at the appropriate place. C++ programs can use the same pattern and many do, but it's also possible to make use of the fact that constructors of static objects, regardless of where they are in the program, are run before main(). One can define a struct (or class) like so: struct Subsystem { Subsystem(); void (*init)(); } and then in some central place, do: static std::set* subsystems = nullptr; Subsystem::Subsystem(void (*init)()) : init(init) { if (!subsystems) subsystems = new std::set; subsystems->insert(this); } Here, 'subsystems' is a raw pointer, which has no destructor (since it is not an object); the std::set it points to is allocated within Subsystem's constructor but never deleted, so we don't need to worry about destruction ordering. We can then write something like: void init() { for (auto* subsystem : *subsystems) subsystem->init(); } ## Ordering If all we care about is that all the init methods get run *in some order* we're golden, but that's probably not the case. If we want to impose order we're going to have to do so using more types with trivial destructors. Usefully, both arrays and strings are trivially destructible, so if we want to, we can do: struct Subsystem { Subsystem(); void (*init)(); const char *name; const char **deps; } where deps points to an array of names of services that this subsystem depends on, terminated by nullptr. For example: /* in /base/init.cc */ static const char* my_deps[] = { nullptr }; static Subsystem me(init_base, "base", my_deps); /* in /ui/init.cc */ static const char* my_deps[] = { "base", nullptr }; static Subsystem me(init_ui, "ui", my_deps); Now our global initialization function (which *is* running after main(), and generally free to use standard containers as it likes) can topologically sort the set of subsystems or do whatever else it pleases, then run their init functions in sequence. In this way, each subsystem's dependencies and initialization code are kept with that subsystem, rather than being kept in some central place. ## Unit Testing This technique is especially useful for unit testing, because unit tests by definition don't run in any specific order. We can use this pattern to allow C++ source files to have their unit tests alongside the code they are testing. This is exactly what the gtest C++ unit testing library does. ## An Example To run this example, do: $ sed -e '1,/^>>>/d' -e '/^<<>> H4sIAAAAAAACA+1XbU/bMBDu1/hX3DomJYUUO69aChVk0mDS3rR9YdK0KnEdGjWNq7x0oGn/fXZS KDBB2KoVJvJ8cFr7/Fzs63N3NTB5SQhxdHp2pudFUMRUpwXP9IydxnnBst3O2sAYG65tyydxbXz1 eYEOMW3HwpZh2qSDiW2abgfs9V03oxSHzgA6LEnO77JrWv9PYTTGP+xP1vQhA+xY1q3xNwxyEX/i uI6Mv2uaHcCbuIAnHn+aBHkOPvxA8zJMYuohxVcpT/MC6CTIepAGM7YDCx6PQe1FZUq1q8uaNkBI qVbfpAs+ZaqYmGfxIiiYoLpJNBosjWuq0XWuAfo5QA99IU8M99E/pev5aNK/ba/yvyFqASYOtp1W /5vA8zilSTlmsLdgMu5DhC6nuiL1dxHKi7Hn1at7fm/YgzCHfUjLJJkXmZB/JehPy5+Lr/rCQBP5 RIkjUJ+FuYYUpd7BvsMNLpEOwlwfzst8MgoDOlVDmQQQ8j3vz7IQeHV+UeWo7UCVXdTKUL7K6u2K SZzXPioy4egib0m7elvFVBvFaQGzIE7VyxPVB4p4BmpQFlwcVvju1bNKqA9XaVDJWFFmKWDJ9NBx vg3N+qd43QTQpH/impf6J4Yt9U+sVv8bwUr/VGgz5sPf5F/pJA/OJ+IG+DVR5pUoRLVPi0jtHst1 iDI+gxf517S7A0uh+UDxKFS7FMu5JZHWVvpHgWb9vwumLIoT9vc+GvXvWKv/f6Yl9e+6bqv/TeDV ycnrt4dHn8HbBxH/8T7d3iYEITphdOpBiJSD/m6IUCg+97lQshxInyNlSxV7NdA5bB3A1jexJWFB Kpr+bAZ6JMpiT1ih/sfjD++/eFDxQWXSCv8R4R71n/zr+m/gK/XfrPp/8bXV/yZw//p/yvk4PGd3 dABHtcUtPQCpegAx1l3Akq7tA1q0aNHiYfALaHEJJwAaAAA= <<<