- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
// A registrator class.
// To register a Parents <- Child relation user must derive their type from
// the correct specialization of this class
template<typename Child, typename... Parents>
struct registrator {
private:
// The registerRelation() function would be called during dynamic initialization of this
// static storage duration variable.
static volatile inline detail::registratorType _registrator
= RelationManager::registerRelation<Child, Parents...>();
protected:
/*
[basic.start.dynamic]/6:
It is implementation-defined whether the dynamic initialization of a non-block inline variable
with static storage duration is sequenced before the first statement of main or is deferred.
If it is deferred, it strongly happens before any non-initialization odr-use of that variable.
[basic.start.dynamic]/4:
A non-initialization odr-use is an odr-use ([basic.def.odr]) not caused directly or indirectly
by the initialization of a non-block static or thread storage duration variable.
By odr-using the _registrator here we are making sure that any Child constructor is odr-using it as well,
thus guaranteeing that the compiler would call registerRelation() strongly before the Child constructor
*/
constexpr registrator() noexcept
{
// Taking an address of a variable, even in a discarded statement, is an odr-use
(void)&_registrator;
}
private:
/*
If registrator() constructor is never 'non-initialization odr-used'
(no Child objects are created besides non-local static ones), then the only
instantiation of the registrator class is an implicit instantiation caused by deriving Child;
[temp.inst]/3:
The implicit instantiation of a class template specialization causes
(3.1) -- the implicit instantiation of the declarations, but not of the definitions, of
the non-deleted class member functions, member classes, scoped member enumerations,
static data members, member templates, and friends; and
(3.2) -- the implicit instantiation of the definitions of deleted member functions,
unscoped member enumerations, and member anonymous unions.
[basic.def.odr]/8:
[...] A constructor for a class is odr-used as specified in [dcl.init].
(In other words, "A constructor (including default constructors) for a class
is odr-used by the initialization that selects it.")
The problem is that if there is no Child() constructor calls, then the compiler is not required to
even DEFINE our registrator() constructor, and since there is only one odr-use of the static inline
variable _registrator, the compiler is not required to generate a definition for it as well. And since
it is not generating a definition, the initialization is also not generated.
But [temp.inst]/3.2 requires that any implicit instantiation of a class template also
causes the DEFINITION instantiation of an unscoped member enumerations. Next,
[dcl.spec.auto.general]/12:
Return type deduction for a templated entity that is a function or function template
with a placeholder in its declared type occurs when the definition is instantiated
even if the function body contains a return statement with a non-type-dependent operand
Thus, using sizeof(*_force_registrator_instantiation()) causes the implicit instantiation
of definition of _force_registrator_instantiation(), and since this function is odr-using _registrator,
its definition (and initialization) is also generated.
The only problem is that by [basic.start.dynamic]/6 the implementation MAY defer the dynamic
initialization of _registrator to the point where it is 'non-initialization odr-used', and since without
the Child() constructor call we don't have any way to non-initialization odr-use it, we also have no way to
make sure that the compiler invokes registerRelation() before main() or before any other point of execution.
The good news, though, is that all three modern compilers obediently initializes _registrator before the first
statement of main().
*/
static auto _force_registrator_instantiation()
{
return &_registrator;
}
enum {
_ROTARTSIGER_ETAITNATSNI = sizeof(*_force_registrator_instantiation()),
};
};