A Schwartz counter is intended to ensure that a global object is initialized before it is used.
Please consider the use of a Schwartz counter shown below.
File Foo.h:
class Foo
{
Foo::Foo();
};
File Foo.cpp:
#include "Foo.h"
// Assume including Mystream.h provides access to myStream and that
// it causes creation of a file-static object that initializes
// myStream (aka a Schwartz counter).
#include "MyStream.h"
Foo::Foo()
{
myStream << "Hello world\n";
}
If Foo::Foo() runs after main() starts, the use of myStream is guaranteed to be safe (i.e. myStream will have been initialized before use) because of the file-static initializer object mentioned in the comments.
However, suppose the Foo instance gets created before main() starts, as would happen if it were global. This is shown here:
File Global.cpp:
#include "Foo.h"
Foo foo;
Note that Global.cpp does not get a file-static initializer object like Foo.cpp does. In this case, how does the Schwartz counter ensure that the MyStream initializer (and therefore the MyStream object itself) are initialized before foo? Or can the Schwartz counter fail in this case?
The use of "Schwartz counters" (so called after Jerry Schwartz who designed the basics of the IOStreams library as it is now in the standard; note that he can't be blamed for many of odd choices because these were tagged onto the original design) can result in accesses to objects before they are constructed. The most obvious scenario is calling a function during construction of a global object which calls into another translation unit using the its own global constructed via a Schwartz counter (I'm using std::cout
as the global being guarded by a Schwartz counter to keep the example short):
// file a.h
void a();
// file a.cpp
#include <iostream>
void a() { std::cout << "a()\n"; }
// file b.cpp
#include <a.h>
struct b { b() { a(); } } bobject;
If the global objects in file b.cpp
are constructed prior to those in the file a.cpp
and if std::cout
is constructed via a Schwartz counter with a.cpp
being the first instance, this code will fail. There is are at least two other reasons why Schwartz counters don't work particularly well:
char
buffer of appropriate size for the actual definition of the object (these typically get mangled to the name as an object of the correct type). However, in both of these cases things are messy.std::cout
) this may cause a significant start-up delay: well-written code typically doesn't use any global initialization but the Schwartz counter needs to run a piece of code for each of the object files which needs to be loaded.Personally I have come to the conclusion that this technique is a nifty idea but it doesn't work in practice. There are three approaches I use instead:
std::call_once()
is a much better alternative.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With