What's the C++ idiom equivalent to the Java static block?

You can have static blocks in C++ as well - outside classes.

It turns out we can implement a Java-style static block, albeit outside of a class rather than inside it, i.e. at translation unit scope. The implementation is a bit ugly under the hood, but when used it's quite elegant!

Downloadable version

There's now a GitHub repo for the solution, containing a single header file: static_block.hpp.

Usage

If you write:

static_block {
    std::cout << "Hello static block world!\n";
}

this code will run before your main(). And you can initialize static variables or do whatever else you like. So you can place such a block in your class' .cpp implementation file.

Notes:

  • You must surround your static block code with curly braces.
  • The relative order of execution of static code is not guaranteed in C++.

Implementation

The static block implementation involves a dummy variable initialized statically with a function. Your static block is actually the body of that function. To ensure we don't collide with some other dummy variable (e.g. from another static block - or anywhere else), we need a bit of macro machinery.

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)
#ifdef __COUNTER__
#define UNIQUE_IDENTIFIER(prefix) EXPAND_THEN_CONCATENATE(prefix, __COUNTER__)
#else
#define UNIQUE_IDENTIFIER(prefix) EXPAND_THEN_CONCATENATE(prefix, __LINE__)
#endif // __COUNTER__

and here is the macro work to put things together:

#define static_block STATIC_BLOCK_IMPL1(UNIQUE_IDENTIFIER(_static_block_))

#define STATIC_BLOCK_IMPL1(prefix) \
    STATIC_BLOCK_IMPL2(CONCATENATE(prefix,_fn),CONCATENATE(prefix,_var))

#define STATIC_BLOCK_IMPL2(function_name,var_name) \
static void function_name(); \
static int var_name __attribute((unused)) = (function_name(), 0) ; \
static void function_name()

Notes:

  • Some compilers do not support __COUNTER__ - it's not part of the C++ standard; in those cases the code above uses __LINE__, which works too. GCC and Clang do support __COUNTER__.
  • This is C++98; you don't need any C++11/14/17 constructs. However, it's not valid C, despite not using any classes or methods.
  • The __attribute ((unused)) can be dropped, or replaced with [[unused]] if you have a C++11 compiler which doesn't like the GCC-style unused extension.
  • This does not avert or help with the static initialization order fiasco, since while you know your static block will execute before main(), you are not guaranteed when exactly that happens relative to other static initializations.

Live Demo


For #1, if you really need to initialise when the process starts/library is loaded, you'll have to use something platform-specific (such as DllMain on Windows).

However, if it's enough for you to run the initialisation before any code from the same .cpp file as the statics is executed, the following should work:

// Header:
class MyClass
{
  static int myDatum;

  static int initDatum();
};

 

// .cpp file:
int MyClass::myDatum = MyClass::initDatum();

This way, initDatum() is guaranteed to be called before any code from that .cpp file is executed.

If you don't want to pollute the class definition, you can also use a Lambda (C++11):

// Header:
class MyClass
{
  static int myDatum;
};

 

// .cpp file:
int MyClass::myDatum = []() -> int { /*any code here*/ return /*something*/; }();

Don't forget the last pair of parentheses - that actually calls the lambda.


As for #2, there's one problem: you can't call a virtual function in the constructor. You're better off doing this by hand in the class instead of using a base class for it:

class MyClass
{
  static int myDatum;

  MyClass() {
    static bool onlyOnce = []() -> bool {
      MyClass::myDatum = /*whatever*/;
      return true;
    }
  }
};

Assuming the class only has one constructor, that will work just fine; it is thread-safe, as C++11 guarantees such safety for initializing static local variables.


You can initialize static data members in C++:

#include "Bar.h"

Bar make_a_bar();

struct Foo
{
    static Bar bar;
};

Bar Foo::bar = make_a_bar();

You may have to think about inter-translation-unit dependencies, but that's the general approach.