Generating structs at compile time with C++26 reflection

Last week we looked at template for and how it simplifies working with tuples. This week, we're going a step further: creating entirely new types at compile time. You will find the link to the Godbolt example in the comment below.

The C++26 reflection functions you will learn about in this article are:

  • data_member_spec : a function that creates a std::meta::info object that describes a C++ type + name.
  • define_aggregate : a reflection function that takes an incomplete struct and adds the desired fields to it.

A new concept is a consteval block which is executed at compile time.

There is a limitation: currently, you can only add members to a struct, not functions.

Getting started

It is important for this function to work that the struct is a forward declaration only. If you give the struct a body, it is considered a complete type and define_aggregate will no longer work:

#include <meta>
#include <iostream>
#include <vector>

struct S_Particle;        

The meta include file is needed for std::meta::info, data_member_spec and define aggregate. I named the struct S_Particle, where the 'S' stands for synthetic.

Consteval block

The consteval block runs at compile time, defines a vector with some members and adds the desired members to the incomplete struct S_Particle. A consteval function or block can use a vector type, which is perhaps surprising as a vector allocates heap memory. This is allowed if no attempt is made to store that heap memory (i.e. the vector) in a variable. In other words, temporary heap allocations are allowed in a consteval function or block:

consteval {
    using namespace std::meta;
    std::vector<info> members;
    auto field_x = data_member_spec(^^float, {.name = "x"});
    auto field_y = data_member_spec(^^float, {.name = "y"});
    auto field_id = data_member_spec(^^int, {.name = "id"});

    members.push_back(field_x);
    members.push_back(field_y);
    members.push_back(field_id);

    define_aggregate(^^S_Particle, members);
}        

As a reminder, the ^^ operator reflects information about a type (among other things).

The elements of the members vector are std::meta::info objects, which allow you to store the type information and the name of the field. The three info objects are then pushed into the vector.

Finally, define_aggregate adds the fields to the incomplete struct S_Particle.

Main function

Within the main function, the fields can now be used in the same way as an ordinary struct:

int main() {
    S_Particle g{1.0f, 2.0f, 42};
    std::cout << g.x << ", " << g.y << ", " << g.id << "\n";
}        

Conclusion

define_aggregate is again a powerful function in the toolchest. Without code injection (planned for C++29) there are limitations but it is already useful in a number of cases:

  • Creating simpler versions of structs, e.g for serialization over the network
  • A typical use case seems to be to convert structs into structs of arrays (relevant for use cases where the cache is an important factor)


To view or add a comment, sign in

More articles by Koen Samyn

  • Bytecode to C++: the one where the compiler CAN optimize the loop

    Last week, the newsletter ended in an anticlimax: the compiler was not able to optimize the emulated bytecode program…

  • Bytecode to C++: The one where the optimizer can't see the loop

    In this edition we continue with the bytecode story. You will discover the basic way to write an emulator with support…

    5 Comments
  • template for is not a loop!

    As part of C++26, the template for construct is really about code generation. Let's start from the top and with a…

    13 Comments
  • The fastest Java Virtual Machine is the C++26 compiler

    This is a clickbait title, but if you are reading this, it worked! There is however a serious idea behind the hook:…

    1 Comment
  • C++26 reflection - std::meta::substitute

    AI had an incredible time at the C++Online conference last week where I was promoting my workshop "Splice and Dice - A…

    2 Comments
  • Node based thinking

    At Digital Arts and Entertainment we develop technical artists, which typically implies that one should be well versed…

    1 Comment
  • Practice makes perfect

    A mathematical application would not be complete without a way to exercise. Practice is necessary to fully understand…

  • Calculator app

    As a math teacher with a quarter century of experience in higher education, I’ve learned a simple truth: real learning…

  • FXMath - Interactive elements

    FXMath was built on the idea that every mathematical concept can be expressed in multiple ways. A vector can appear as…

  • Layout details

    As I am preparing to open source my FXMath application, I decided to work on annoying layout issues that are…

Others also viewed

Explore content categories