Static Reflection in C++ -- Wu Yongwei
Static reflection is under consideration for C++26. Wu Yongwei demonstrates how to achieve reflection now and shows some examples of what C++26 might make possible.
Static reflection will be an important part of C++ compile-time programming, as I discussed in the October issue of Overload [Wu24]. This time I will discuss static reflection in detail, including how to emulate it right now, before it’s been added to the standard.
Static Reflection in C++
by Wu Yongwei
From the article:
Background
Many programming languages support reflection (Python and Java, for example). C++ is lagging behind.
While this is the case, things are probably going to change in C++26. Also, what will be available in C++ will be very different from what is available in languages like Java or Python. The keyword is ‘static’.
Andrew Sutton defined ‘static reflection’ as follows [Sutton21]:
Static reflection is the integral ability for a metaprogram to observe its own code and, to a limited extent, generate new code at compile time.
‘Compile-time’ is the special sauce in C++, and it allows us to do things impossible in other languages:
- Zero-overhead abstraction. As Bjarne Stroustrup famously put it, ‘What you don’t use, you don’t pay for. What you do use, you couldn’t hand-code any better.’ If you do not need static reflection, it will not make your program fatter or slower. But it will be at your hand when you do need it.
- High performance. Due to the nature of compile-time reflection, it is possible to achieve unparalleled performance, when compared with languages like Java or Python.
- Versatility at both compile time and run time. The information available at compile time can be used at run time, but not vice versa. C++ static reflection can do things that are possible in languages like Java, but there are things that C++ can do but are simply impossible in other languages.
What we want from reflection
When we talk about static reflection, what do we really want? We really want to see what a compiler can see, and we want to be able to use the relevant information in the code. The most prominent cases are
enum
andstruct
. We want to be able to iterate over all the enumerators, and know their names and values. We want to be able to iterate over all the data members of a struct, and know their names and types. Obviously, when a data member is an aggregate, we also want to be able to recurse into it during reflection. And so on.Regretfully, we cannot do all these things today with ‘standard’ definitions. Yes, in some implementations it is possible to hack out some of the information with various tricks. I would prefer to use macros and template techniques to achieve the same purpose, as the code is somewhat neater, more portable, and more maintainable – at the cost of using non-standard definition syntaxes. Of course, nothing beats direct support from the future C++ standard.