c++ Play it only once Sam

Just how many times you had admitted to yourself? It was rather foolish to think of globals in C++ as “a simple thing” :) After all: “What could possibly go wrong”tm ? If you know what I am talking about please proceed.

As it turns out many things can and will go wrong, with a singleton in particular. Specifically with what you might think of, as C++ global variables and functions and other “things”. I am sure every now and then, you are wondering why is this singleton not a singleton, or why is this function called more than once,  and how it is, that nobody seems to care.

After many years of various coding idioms, that handle static linkage and global variables, and the famous “Singleton Pattern” implementations, may I be so bold to remind you: you do not have to dance around these issues anymore.

That was 2018 after all

As from C++11, you had a new memory model to enjoy. And from C++17 you had them inline variables too.

Since the C++ primordial soup, you have local statics. Aka static local variables.  And as if this would be an end to this list of crucial C++ ingredients,  there is one more ingredient in this secret sauce: lambdas.

Detour: Please do not forget the big gotcha: Internal linkage and anonymous namespace.

So you are thinking now: What is this post? Reminding us of the whole of the standard text? Is that it?

Well, no, my dear readers. Your humble servant aka myself has prepared yet another delicious little nugget for you to readily copy-paste into your code.

Lambda based singleton

Here it comes:

Please observe carefully the above:

  • the big gotcha first and again: anonymous namespace implies default static linkage for variables inside. Thus do not put that code inside it. This is the header code. This dbj_once namespace deliberately has that name.
    If this would be anon. namespace variables inside will be treated as if they are static. Which will make things be called as many times as you have compilation units. Aka “cpp files”.
    Effectively inline singleton & instance(), will be linked as: static singleton & instance(). Please make sure to devote some time to the  “Internal linkage” and anon. namespace’s subject.
  • Lock-Free. Since C++11, this is safe in presence of multiple threads (MT) and is supported as such by all major compilers. No std::mutex or some other kind of locking is necessary.
  • Call Once Mechanism. Inside instance() is an anonymous lambda that is guaranteed to be called only once.
  • This coding idiom is safe to use in header-only situations. Very important for modern and standard C++.

Please do remember: until C++11, to achieve the same, this was much more involved. Much more.

Now (one might ask) why not just use std::call_once ?  Or if you are a WIN32 aficionado, why not just use InitOnceExecuteOnce() a function?  If you really need to please do try. But be forewarned these two are clearly illustrating what I mean by “it was much more involved”, in the bad old days before C++11.

You can, but you do not have to use the legacy singleton idioms. It is as simple as that. The code presented above is standard C++ and does it all, for you.

Bonus

Let us think. Compile-time singleton would be much more “singleton logical”. Unique things are unlikely to come and go dynamically at runtime.  They can but are unlikely.

Computer parts represented as singletons are one example. Monitor as a singleton instance is unlikely to disappear at runtime. But. It can.

So let’s code following those requirements. User view would be this:

Fully functional and working code of compile time Singleton is ready for you, on a Godbolt.

Enjoy, study, and come back to us with questions. But.

But wait, I just changed my mind. Completely.

We need to talk about the “beast”. And that “beast” is modern C. It keeps churning out simpler, cleaner, faster, and easier-to-maintain solutions. V.s. anything basically.

How would one solve the above conundrum using modern C?  Let us push you into the deep end of the pool:

That is one simple and easy-to-maintain solution. From consistent requirements to a consistent solution.

In C, one produces programs, not abstractions from which programs might be built.

C executables are fast, small, and resilient. I prefer C.

Here is the mandatory Godbolt.