I recently came across the Nifty Counter Idiom. My understanding is that this is used to implement globals in the standard library like cout, cerr, etc. Since the experts have c
All of your questions about utility / performance of Nifty Counter aka Schwartz Counter were basically answered by Maxim Egorushkin in this answer (but see also the comment threads).
Global variables in modern C++
The main issue is that there is a trade-off taking place. When you use Nifty Counter your program startup time is a bit slower (in large projects), since all these counters have to run before anything can happen. That doesn't happen in Meyer's singleton.
However, in the Meyer's singleton, every time you want to access the global object, you have to check if it's null, or, the compiler emits code that checks if the static variable was already constructed before any access is attempted. In the Nifty Counter, you have your pointer already and you just fire away, since you can assume the init happened at startup time.
So, Nifty Counter vs. Meyer's singleton is basically a trade-off between program startup time and run-time.
Summarizing the answers and comments:
Option 1 - the nifty counter pattern, allowing the use of a global variable that is:
Option 2 - the Meyers singleton pattern with a reference variable (as presented in the question):
However, it will create a copy of the singleton object in shared objects, even if all shared objects and the main are linked dynamically with the library. This is because the Singleton reference variable is declared static in a header file and must have its initialization ready at compile time wherever it is used, including in shared objects, during compile time, before meeting the program they will be loaded to.
Option 3 - the Meyers singleton pattern without a reference variable (calling a getter for retrieving the Singleton object):
However, in this option there is no global variable nor inline call, each call for retrieving the Singleton is a function call (that can be cached on the caller side).
This option would look like:
// libA .h
struct A {
A();
};
A& getA();
// some other header
A global_a2 = getA();
// main
int main() {
std::cerr << "main\n";
}
// libA .cpp - need to be dynamically linked! (same as libstdc++ is...)
// thus the below shall be created only once in the process
A& getA() {
static A a;
return a;
}
A::A() { std::cerr << "construct A\n"; }
The static local/Meyer's singleton + static global reference (your solution) is nearly equivalent to the nifty counter.
The differences are as follows:
No .cpp file is required in your solution.
Technically the static Steam&
exists in every compilation unit; the object being referred to does not. As there is no way to detect this in the current version of C++, under as-if this goes away. But some implementations might actually create that reference instead of eliding it.
Someone could call getStream()
prior to the static Stream&
being created; this would cause difficulty in destruction order (with the stream being destroyed later than expected). This can be avoided by making that against the rules.
The standard is mandated to make creating the static Stream
local in the inline
getStream
thread safe. Detecting that this is not going to happen is challenging for the compiler, so some redundant thread-safety overhead may exist in your solution. The nifty counter does not support thread safety explicitly; this is considered safe as it runs at static initialization time, prior to when threads are expected.
The call to getStream()
must occur in each and every compilation unit. Only if it is proven that it cannot do anything may it be optimized out, which is difficult. The nifty counter has a similar cost, but the operation may or may not be be simpler to optimize out or in runtime cost. (Determining this will require inspecting resulting assembly output on a variety of compilers)
"magic statics" (statics locals without race conditions) where introduced in C++11. There could be other issues prior to C++11 magic statics with your code; the only one I can think of is someone calling getStream()
directly in another thread during static initialization, which (as mentioned above) should be banned in general.
Outside the realm of the standard, your version will automatically and magically create a new singleton in each dynamicly linked chunk of code (DLL, .so, etc). The nifty counter will only create the singleton in the cpp file. This may give the library writer tighter control over accidentally spawning new singletons; they can stick it into the dynamic library, instead of spawning duplicates.
Avoiding having more than one singleton is sometimes important.
With the solution you have here, the global stream
variable gets assigned at some point during static initialization, but it is unspecified when. Therefore the use of stream
from other compilation units during static initialization may not work. Nifty counter is a way to guarantee that a global (e.g. std::cout) is usable even during static initialization.
#include <iostream>
struct use_std_out_in_ctor
{
use_std_out_in_ctor()
{
// std::cout guaranteed to be initialized even if this
// ctor runs during static initialization
std::cout << "Hello world" << std::endl;
}
};
use_std_out_in_ctor global; // causes ctor to run during static initialization
int main()
{
std::cout << "Did it print Hello world?" << std::endl;
}