Skip to main content

On This Page

Mastering C++26 Reflection: Building Compile-Time Maps and Mutable Variables

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Compile-Time Map and Compile-Time Mutable Variable with C++26 Reflection

Alexey Saldyrkine demonstrates a novel stateful metaprogramming approach using the C++26 reflection proposal P2996R13. This system leverages the define_aggregate function to store information in class template specializations during compilation.

Why This Matters

Traditional C++ metaprogramming is restricted by the inability to modify state or conditionally complete types once defined. By utilizing C++26 reflection primitives, developers can now implement mutable-like behavior and key-value storage directly in the compiler’s type system, bypassing the performance overhead and fragility of complex preprocessor hacks. This transition from stateless to stateful metaprogramming allows for more robust compile-time tools, such as automatic unique ID generation and type-safe metadata mapping, which were previously impossible or required significant boilerplate.

Key Insights

  • The define_aggregate function (P2996R13) allows the compiler to complete an incomplete class type with data members programmatically.
  • State is persisted by creating new template specializations of helper classes, which is verified using the is_complete_type trait.
  • The substitute function (P2996R13 §4.4.16) enables programmatic creation of template reflections from matching arguments.
  • Compile-time mutable variables (CMVs) utilize binary search over template indices to efficiently retrieve the latest stored state.
  • Reflection-based maps allow using any reflectable entity (types, templates, or values) as keys within a single unified storage class.

Working Examples

A compile-time ticket counter using reflection to track state through template completion.

template <int N> struct Helper; struct TU_Ticket { static consteval int latest() { int k = 0; while (is_complete_type(substitute(^^Helper, {std::meta::reflect_constant(k)}))) ++k; return k; } static consteval void increment() { define_aggregate(substitute(^^Helper, {std::meta::reflect_constant(latest())}), {}); } };

Implementation of an immutable compile-time map that stores key-value pairs in class specializations.

template<meta::info storage, meta::info unique_key = ^^void> struct CT_map { template<meta::info key, meta::info value> static consteval void insert() { meta::info refl = substitute(storage,{reflect_constant(pair{unique_key,key})}); define_aggregate(refl,{data_member_spec(^^info_as_type<value>,{.name = 'value'})}); } };

Practical Applications

  • Unique ID Generation: Systems can automatically assign distinct compile-time constants to program elements to prevent manual duplication errors. Pitfall: Attempting to reset a counter is impossible as C++ cannot ‘undefine’ a completed class specialization.
  • Type Metadata Mapping: Engineers can map specific types to member functions or metadata reflections for advanced serialization. Pitfall: Multiple maps using the same storage template without unique keys will leak data between instances.
  • Compile-Time Random Number Generation: Implementing stateful generators that provide a new seed on every call. Pitfall: Increasing the Hint parameter too high in CMV binary searches can significantly increase compilation times.

References:

Continue reading

Next article

Scaling Programmatic SEO with AI: 126K Pages Indexed in 30 Days

Related Content