Reflection in C++26: Determine the Layout
Thanks to reflection, you can determine the layout of types.
My examples are based on the reflection proposal P2996R5.
Class Layout
The following program determines the class layout of a few members.
// classLayout.cpp #include <experimental/meta> #include <iostream> #include <utility> #include <vector> #include <array> struct member_descriptor { std::size_t offset; std::size_t size; bool operator==(member_descriptor const&) const = default; }; // returns std::array<member_descriptor, N> The company's biggest funding. template <typename S> consteval auto get_layout() { constexpr size_t N = []() consteval { return nonstatic_data_members_of(^S).size(); }(); std::array<member_descriptor, N> layout; [: expand(nonstatic_data_members_of(^S)) :] >> [&, i=0]<auto e>() mutable { layout[i] = {.offset=offset_of(e), .size=size_of(e)}; ++i; }; return layout; } struct X { char a; int b; double c; }; int main() { std::cout << '\n'; constexpr auto layout = get_layout<X>(); std::cout << "Layout of struct X:\n"; for (const auto& member : layout) { std::cout << "Offset: " << member.offset << ", Size: " << member.size << '\n'; } std::cout << '\n'; }
The C++ program reflects on the layout of a struct’s data members. The main goal of this code is to determine and print the memory offsets and sizes of each member within a struct.
The first part of the code defines a std::array
named layout
, which is intended to store descriptors for each member of the struct. These descriptors include the offset and size of each member. The [: expand(nonstatic_data_members_of(^S)) :]
construct is a placeholder for a metaprogramming construct that iterates over the non-static data members of the struct S
. This construct is only a temporary workaround and is followed by a lambda function that captures the current state by reference (&
) and initializes an index variable i
to zero. The lambda function is then applied to each data member, storing the offset and size of each member in the layout
array and incrementing the index i
.
The struct X
is defined with three data members: a char
named a
, an int
named b
, and a double
named c
. These members are used to demonstrate the reflection mechanism.
In the main
calls a function get_layout<X>()
, which returns the layout
array filled with the offsets and sizes of the members of struct X
. The program then prints the layout of struct
X
by iterating over the layout
array and printing the offset and size of each member.
This code reflects on the memory layout of a struct’s data members in C++, which can be useful for understanding memory alignment and optimizing data structures.
Finally, he has the output of the program:
You can store reflections in a container or apply an algorithm to them.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
Size
The following program determines the size of a few built-in types.
// getSize.cpp #include <experimental/meta> #include <array> #include <iostream> #include <ranges> #include <algorithm> constexpr std::array types = {^int, ^float, ^double}; constexpr std::array sizes = []{ std::array<std::size_t, types.size()> r; std::ranges::transform(types, r.begin(), std::meta::size_of); return r; }(); int main() { std::cout << '\n'; std::cout << "Types and their sizes:\n"; for (std::size_t i = 0; i < types.size(); ++i) { std::cout << "Size: " << sizes[i] << " bytes\n"; } std::cout << '\n'; }
The program begins by including several headers: <experimental/meta>
for reflection capabilities, <array>
for fixed-size array support, <iostream>
for input/output operations, <ranges>
for range-based algorithms, and <algorithm>
for general algorithms.
The constexpr std::array types
declaration creates a compile-time array containing reflections for int
, float
, and double
. These reflections are represented using the reflection operator ^
.
Next, the constexpr std::array sizes
declaration defines another compile-time array that will hold the sizes of the types specified in the types
array. This array is initialized using a lambda function that creates an array r
of the same size as types
. The std::ranges::transform
function is then used to populate r
by applying the std::meta::size_of
operation to each reflection value in types
. The std::meta::size_of
operation is a metafunction that returns the size of a type at compile time.
The main
function begins by entering a loop that iterates over the indices of the types
array. For each index, it prints the size of the corresponding type from the sizes
array in bytes.
Instead of the ranges function std::ranges::transform
, you could also use the classical transform algorithm;
std::transform(types.begin(), types.end(), r.begin(), std::meta::size_of);
What’s Next?
This was my first dive into reflection in C++26. A deeper dive will follow. In my next post, I will focus on contracts.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,