Cute Tricks for Quasi-static Registration

Useful code you should "never" write

Daisy Hollman, Google

C++Now 2022
Twitter: @The_Whole_Daisy
Email: cpp@dsh.fyi

Cute Trick: The "Template Registrar" Pattern

The "Template Registrar" Pattern

Need to do something (like create a list) for all of the specializations of a class template? Use the template registrar pattern.
(Caveat: C++ doesn't guarantee consistent invocation order of register_type, so use this carefully!)

How does it work?

  • We use Curiously Recurring Template Pattern (CRTP) to make the type available in a base class that handles the registration:
  • The CRTP base has a static member that stores the result of the registration process
  • Because of the way templates work in C++, there will be a different instance of the static variable index for each type Derived that RegisteredType is instantiated with.
  • Since it's an inline variable, we have to "use" it to ensure it is instantiated. The default constructor will suffice.

How does it work?

  • Because index is a static variable, its initializer will be called at static initialization time
  • This means that the function register_type() will be called once for each Derived in the compiled program.
  • C++ requires that inline variables must be de-duplicated across translation units.

Looking at the generated code

(Looking at a simplified version)

What happens when there's more than one?

The Question of Ordering

  • As with any symbol, the compiler and linker must ensure that the various __cxx_global_var_init symbols are properly de-duplicated across translation units at link time.
  • Dynamically linked libraries may be initialized in different orders at runtime, however!
  • The standard is pretty silent on guarantees in this space.

Cute Trick: Counting Aggregate Members

Counting Aggregate Members

Cute C++ trick of the day: C++ doesn't have much reflection, but you can figure out how many members are in an aggregate by trying to construct it with objects that are convertible to anything.
(Details of most general version omitted; advance version later)

What's going on?

  • "objects that are convertible to anything"

What's going on?

  • "trying to construct it"
  • How do we repeat something N times?

What's going on?

  • The exact number of members in an aggregate is the number N when we can construct it with N objects but not with N+1:

What can we use this for?

  • If we know the number of members in an aggregate, we can destructure it!
    (Perfect forwarding omitted for brevity.)
  • You need overloads for all of the sizes you want to support.
  • Note: You can't use SFINAE because structured bindings have to be part of a statement, not an expression, and we can't make this any terser because you can't expand a parameter pack on the left-hand-side of a structured binding.
  • We can use this to automatically generate serialization and deserialization functions!