Search code examples
javac++initializationequivalentstatic-block

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


I have a class with some static members, and I want to run some code to initialize them (suppose this code cannot be converted into a simple expression). In Java, I would just do

class MyClass {
    static int field1;
    static int field2;

    static {
        /* do some computation which sets field1 and field2 */
    }
}

Unless I'm mistaken, C++ does not allow for such static code blocks, right? What should I be doing instead?

I would like solution for both of the following options:

  1. Initialization happens when process loads (or when the DLL with this class is loaded).
  2. Initialization happens when the class is first instantiated.

For the second option, I was thinking of:

class StaticInitialized {
    static bool staticsInitialized = false;

    virtual void initializeStatics();

    StaticInitialized() {
        if (!staticsInitialized) {
            initializeStatics();
            staticsInitialized = true;
        }
    }
};

class MyClass : private StaticInitialized {
    static int field1;
    static int field2;

    void initializeStatics() {
        /* computation which sets field1, field2 */
    }
};

but that's not possible, since C++ (at the moment?) does not allow initialization of non-const static members. But, at least that reduces the problem of a static block to that of static initialization by expression...


Solution

  • 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__
    #ifdef _MSC_VER
    #define _UNUSED
    #else
    #define _UNUSED __attribute((unused))
    #endif // _MSC_VER
    

    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 _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)) might 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