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

18,222

Solution 1

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

Solution 2

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.

Solution 3

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.

Solution 4

Here is a nice way to mimic a static block using C++11:

Macro

#define CONCATE_(X,Y) X##Y
#define CONCATE(X,Y) CONCATE_(X,Y)
#define UNIQUE(NAME) CONCATE(NAME, __LINE__)

struct Static_ 
{
  template<typename T> Static_ (T only_once) { only_once(); }
  ~Static_ () {}  // to counter "warning: unused variable"
};
// `UNIQUE` macro required if we expect multiple `static` blocks in function
#define STATIC static Static_ UNIQUE(block) = [&]() -> void

Usage

void foo ()
{
  std::cout << "foo()\n";
  STATIC
  {
    std::cout << "Executes only once\n";
  };  
}

Demo.

Solution 5

In C++ there is no such idiom.

The reason lies in the entirely different nature of the code generated from C++: The runtime is not "managed". In the generated code, after compilation, there exists no notion of a "class" anymore, and there is no such thing like code entities loaded on demand by a "classloader".

There are some elements with roughly comparable behaviour, yet you really need to understand their nature precisely to exploit this behaviour.

  • you can build your code into a shared library, which can be loaded dynamically, at runtime.
  • in C++11, you can std::call_once your initialisation from a class constructor. However, such code will run late, when the class instance is created, not when the executable or shared library is loaded
  • you can define global variables and (class) static variables with an initialiser. This initialiser can be a function, which allows you to run code when the variable gets initialised. The execution order of these initialisers is well defined only within a single translation unit (e.g. one *.cpp file).

But you must not assume anything beyond that; esp. you can never be sure if and when this initialisation is actually performed. This warning is for real. Especially do not assume anything about side-effects of such initialisation code. It is perfectly legal for the compiler to replace such code by something deemed "equivalent" by the compiler. Future compiler versions can be assumed to become more and more clever in that respect. Your code may seem to work, but can break with different optimisation flags, different build process, newer compiler version.


practical hint: if you find yourself in the situation that you have several static variables, which you need to initialise properly, then chances are that you want to factor them out into a class. This class can then have a regular constructor and destructor to do the initialisation / clean-up. You may then place an instance of that helper class into a single (class) static variable. C++ gives very strong consistency guarantees for invoking ctors and dtors of classes, for anything which is accessible by official means (no casts, no low-level trickery).

Share:
18,222
einpoklum
Author by

einpoklum

Made my way from the Olympus of Complexity Theory, Probabilistic Combinatorics and Property Testing to the down-to-earth domain of Heterogeneous and GPU Computing, and now I'm hoping to bring the gospel of GPU and massive-regularized parallelism to DBMS architectures. I've post-doc'ed at the DB architecture group in CWI Amsterdam to do (some of) that. I subscribe to most of Michael Richter's critique of StackOverflow; you might want to take the time to read it. If you listen closely you can hear me muttering "Why am I not socratic again already?"

Updated on June 13, 2022

Comments

  • einpoklum
    einpoklum almost 2 years

    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 myDatum;
    
        static {
            /* do some computation which sets myDatum */
        }
    }
    

    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 myDatum;
    
        void initializeStatics() {
            /* computation which sets myDatum */
        }
    };
    

    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...

  • einpoklum
    einpoklum over 10 years
    Your static bool onlyOnce is not thread-safe, I believe. It might executed more than once.
  • Angew is no longer proud of SO
    Angew is no longer proud of SO over 10 years
    @einpoklum In C++11, function-scope statics are guaranteed to be initialised in a thread-safe manner.
  • Thomas Kejser
    Thomas Kejser over 9 years
    @Angew: Will the above also be safe if myDatum is static const int ? Could this idiom be used to init a static const efficiently (and without external linker issues if I moved the constructor into the .cpp) before main() is run?
  • Angew is no longer proud of SO
    Angew is no longer proud of SO over 9 years
    @ThomasKejser If it's const, you cannot use option #2 at all, const variables have to be initialised in their definition.
  • Greg Kramida
    Greg Kramida about 9 years
    Your answer to #1 did not work for me in the scenario of having a class definition in a separate static library. The only thing that worked was declaring the static 'myDatum' equivalent in the library's namespace ("~global") scope. With that, I had to use a static bool to check if it's been initialized already, like you do for #2. I tested with -O3, this worked.
  • Greg Kramida
    Greg Kramida about 9 years
    Moreover, I discovered that this (namespace-scope) solution above only works when the header where 'myDatum' is defined and initialized is (perhaps through some other headers) included in the non-library code that's using the library. So you have to make sure every class that relies on this static initialization block actually includes 'MyClass.h' in the header.
  • einpoklum
    einpoklum over 7 years
    This is inside a function, which doesn't count. Actually, there's a nifty solution which doesn't use any C++11, see my answer.
  • iammilind
    iammilind over 7 years
    @einpoklum, just saw your answer & since you are using in production, it must be working well for you. However, the solution I wrote above works well outside the function as well (Demo edited). Yes, it does use C++11 to make resemble as close as Java's "static" block. We can do it with simple function as well, but then it will not have capabilities of capturing all. For example, void foo () { static X x; }. Suppose if you want to set some fields of x just once using static block then you must have a C++11 lambda. It won't be possible without it.
  • einpoklum
    einpoklum over 7 years
    I don't quite understand your latter example. My static block is not intended (in Java) to hold global data, just to run something once at startup. Anyway, +1 for this solution generally.
  • einpoklum
    einpoklum over 7 years
    The thing is that sometimes no "client" knows the API that's used in the static code. The static block might be registering something in another global structure which does act the way you suggested.
  • Ichthyo
    Ichthyo about 6 years
    Warning this solution is dangerous. It is luringly easy to use. You may use it to plant a static field into a class. The compiler is free to optimise that away any time, if he can prove there is no access.
  • einpoklum
    einpoklum about 6 years
    Well, you can just forget about the class and the class loading, and just have the static block at TU scope, which is what I ended up doing. So - I think your answer is focusing on the wrong issue. I mean, I understand, yes, it will not be the same as running Java static blocks at dynamic class loading time, but like you said - we don't have that in C++.
  • Ichthyo
    Ichthyo about 6 years
    @einpoklum I am aware of that. However, I have seen lots of java devs being trapped by unsing Java idioms in C++. For that reason, I think it should be made clear that there is no equivalent
  • einpoklum
    einpoklum about 6 years
    The actual reason I needed this was to register some template instantiations in a factory at program load time.
  • iammilind
    iammilind about 6 years
    Haven't faced any problem so far @Ichthyo. Moreover, lazy initialization is part of C++ and I haven't heard of compilers optimising it. What made you feel the above solution as dangerous.
  • Ichthyo
    Ichthyo about 6 years
    two things are potentially dangerous. The compiler is free to optimise statics away entirely. It depends on fine points and circumstances if he does. And, second, it is tempting to do side-effects which involve statics / globals from other translation units. The fact such code often seem to work does not say anything.
  • Amir
    Amir about 3 years
    Thanks a lot. This helped me a great deal.
  • einpoklum
    einpoklum about 3 years
    @Amir: The please also consider starring the GitHub repository :-P
  • BuvinJ
    BuvinJ over 2 years
    Minor note: I tested this on Android using Qt C++, and I didn't see a cout result on the console, but I was able to determine the block fired off by manipulating a static value then later spitting it out within normal app execution.
  • BuvinJ
    BuvinJ over 2 years
    Regarding __attribute((unused)), for Visual Studio support, I but that in a #define _UNUSED and made that empty #ifdef _MSC_VER.
  • einpoklum
    einpoklum over 2 years
    @BuvinJ: Yes, that should work - you are welcome to edit my answer and do that (but please make sure your change compiles correctly).