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:
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...
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!
There's now a GitHub repo for the solution, containing a single header file: static_block.hpp
.
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:
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:
__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__
.__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.main()
, you are not guaranteed when exactly that happens relative to other static initializations.