1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
# Reflection in Glaze
Glaze provides compile time reflection utilities for C++.
Most aggregate structs require no metadata at all, but if you later decide a couple of keys should be
renamed or exposed under additional aliases you can layer those tweaks on with
[`glz::meta<T>::modify`](modify-reflection.md). Pure reflection continues to handle the untouched members while
the `modify` entries supply the bespoke names.
```c++
struct T
{
int a{};
std::string str{};
std::array<float, 3> arr{};
};
static_assert(glz::refl<T>::size == 3);
static_assert(glz::refl<T>::keys[0] == "a");
```
## Reflection Concepts
### `reflectable<T>`
The `reflectable<T>` concept identifies aggregate types that can be automatically reflected without requiring a `glz::meta` specialization:
```c++
struct SimpleStruct {
int x;
double y;
};
static_assert(glz::reflectable<SimpleStruct>); // true - aggregate type
```
### `has_reflect<T>`
The `has_reflect<T>` concept detects whether `glz::reflect<T>` can be instantiated for a given type. It automatically captures **all** types that have a valid `reflect<T>` specialization, including:
- Types satisfying `reflectable<T>` (aggregate types)
- Types with `glz::meta` specializations (`glaze_object_t`, `glaze_array_t`, `glaze_enum_t`, etc.)
- Readable map types like `std::map` and `std::unordered_map`
- Any future types that get `reflect<T>` specializations
```c++
// Aggregate struct - both reflectable and has_reflect
struct Aggregate {
int value;
};
// Struct with glz::meta - NOT reflectable but has_reflect
struct WithMeta {
int x;
double y;
};
template <>
struct glz::meta<WithMeta> {
using T = WithMeta;
static constexpr auto value = glz::object(&T::x, &T::y);
};
static_assert(glz::reflectable<Aggregate>); // true
static_assert(glz::has_reflect<Aggregate>); // true
static_assert(!glz::reflectable<WithMeta>); // false - has meta specialization
static_assert(glz::has_reflect<WithMeta>); // true - can use reflect<T>
// Both can safely use reflect<T>
constexpr auto aggregate_size = glz::reflect<Aggregate>::size; // Works!
constexpr auto meta_size = glz::reflect<WithMeta>::size; // Works!
```
### When to use each concept
- Use `reflectable<T>` when you specifically need aggregate types without `glz::meta` specializations
- Use `has_reflect<T>` when you want to check if `glz::reflect<T>` can be safely called
- Use `has_reflect<T>` in generic code that needs to work with any type that supports reflection
The `has_reflect<T>` concept is implemented by checking if `reflect<T>` can be instantiated, making it automatically stay in sync with all `reflect<T>` specializations
```c++
// Generic function that works with any reflectable type
template<glz::has_reflect T>
void print_field_count(const T& obj) {
std::cout << "Type has " << glz::reflect<T>::size << " fields\n";
}
// Works with both aggregate types and types with glz::meta
print_field_count(Aggregate{}); // Works
print_field_count(WithMeta{}); // Also works
```
## glz::convert_struct
The `glz::convert_struct` function show a simple application of Glaze reflection at work. It allows two different structs with the same member names to convert from one to the other. If any of the fields don't have matching names, a compile time error will be generated.
```c++
struct a_type
{
float fluff = 1.1f;
int goo = 1;
std::string stub = "a";
};
struct b_type
{
float fluff = 2.2f;
int goo = 2;
std::string stub = "b";
};
struct c_type
{
std::optional<float> fluff = 3.3f;
std::optional<int> goo = 3;
std::optional<std::string> stub = "c";
};
suite convert_tests = [] {
"convert a to b"_test = [] {
a_type in{};
b_type out{};
glz::convert_struct(in, out);
expect(out.fluff == 1.1f);
expect(out.goo == 1);
expect(out.stub == "a");
};
"convert a to c"_test = [] {
a_type in{};
c_type out{};
glz::convert_struct(in, out);
expect(out.fluff.value() == 1.1f);
expect(out.goo.value() == 1);
expect(out.stub.value() == "a");
};
"convert c to a"_test = [] {
c_type in{};
a_type out{};
glz::convert_struct(in, out);
expect(out.fluff == 3.3f);
expect(out.goo == 3);
expect(out.stub == "c");
};
};
```
|