C++ Logo

std-proposals

Advanced search

Make side effects in static initialization more consistent

From: kh <konstantin.harmuth_at_[hidden]>
Date: Mon, 21 Dec 2020 12:05:07 +0100
Initialization of variables might carry side effects. The behavior for
these effects is usually well-defined. One exception is the
initialization of global variables with static storage duration.

The main problem is that the linker could remove unused variables. The
linker then also eliminates any side effects of the initialization.
This might cause problems in some cases when the side effect is
expected. The problem is strongly related to static linking. I
couldn't observe it when linking the object files directly into an
executable or in a shared library. This inconsistency is also
problematic.

Example 1: The side effect is used to register an algorithm by name
that can be created without knowing the concrete type later ("self
registering factory").
https://godbolt.org/z/YTqEW7

Example 2: Many C-Libraries need an explicit call to an init function
before the library is usable. A common solution is to do this
statically at one place, so it can't be forgotten.

Alternatives to get the above example working with static linking:
- Explicitly do the registration and do not rely on static
initialization side effects.
    - Write this code by hand.
    - Perform a mock step to generate the registration code.
- Use platform dependent linker flags to completely disable removal of
unused symbols.
- Trick the optimizer to include the unused symbols (Hacks):
    - Add unnecessary includes.
    - Place the initialization statement inside a header file.
    - Dummy access to static variables so the linker is not allowed to
remove them.
    - Bugusing

Possible solutions to guarantee code execution before entering main():
- Add an attribute for (static) variables that should not be discarded
by the linker. e.g. [[nodiscard]] or [[keep]]
    - No impact on existing code as it adds a new attribute that is
optional. It also does not alter the default behavior.
[[keep]] const auto MyUnusedVariable = InitializeSomeLibrary(); //
This happens in global scope.

- prevent the linker from removing static variables which have an
initializer with a side effect.
    - Might be impossible to detect.
    - Could have a performance impact on existing codebases.

- add a general construct to execute code before entering main(), so
the user is no longer required to fake initialize with dummy variables
and side effects.
    - Syntactic sugar for the lambda initializer combined with an
execution guarantee.
    - The problem still exists. There is just another syntax to
circumnavigate it.
initialize
{
    const auto MyUnusedVariable = InitializeSomeLibrary();
}

It might also be a good idea to warn when the linker throws away
symbols with side effects (Which might be impossible to detect in some
cases).

Regards,
Konstantin Harmuth

Received on 2020-12-21 05:05:21