https://github.com/yosh-matsuda/field-reflection
Compile-time reflection for C++ to get field names and types from a struct/class.
https://github.com/yosh-matsuda/field-reflection
cpp cpp20 header-only reflection static-reflection
Last synced: 25 days ago
JSON representation
Compile-time reflection for C++ to get field names and types from a struct/class.
- Host: GitHub
- URL: https://github.com/yosh-matsuda/field-reflection
- Owner: yosh-matsuda
- License: mit
- Created: 2024-02-02T15:56:20.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2024-12-27T05:20:55.000Z (about 1 year ago)
- Last Synced: 2024-12-27T06:20:21.836Z (about 1 year ago)
- Topics: cpp, cpp20, header-only, reflection, static-reflection
- Language: C++
- Homepage:
- Size: 26.4 KB
- Stars: 22
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# field-reflection C++
Compile-time reflection for C++ to get field names and types from a struct/class.
[](https://github.com/yosh-matsuda/field-reflection/actions/workflows/tests.yml)
## Features
* compile-time reflection
* header-only single file
* no user-side macros
* no dependencies
## Requirements
C++20 compilers are required to use this library.
* GCC >= 11
* Clang >= 15
* with libc++-16 or later
* MSVC >= 19.37
* clang-cl >= 17
## Usage
```cpp
#include
#include
#include
#include
#include
#include "field_reflection.hpp"
using namespace field_reflection;
struct my_struct
{
int i = 287;
double d = 3.14;
std::string hello = "Hello World";
std::array arr = {1, 2, 3};
std::map map{{"one", 1}, {"two", 2}};
};
// get field names
constexpr auto my_struct_n0 = field_name; // "i"sv
constexpr auto my_struct_n1 = field_name; // "d"sv
constexpr auto my_struct_n2 = field_name; // "hello"sv
constexpr auto my_struct_n3 = field_name; // "arr"sv
constexpr auto my_struct_n4 = field_name; // "map"sv
// get field types
using my_struct_t0 = field_type; // int
using my_struct_t1 = field_type; // double
using my_struct_t2 = field_type; // std::string
using my_struct_t3 = field_type; // std::array
using my_struct_t4 = field_type; // std::map
// get field values with index
auto s = my_struct{};
auto& my_struct_v0 = get_field<0>(s); // s.i
auto& my_struct_v1 = get_field<1>(s); // s.d
auto& my_struct_v2 = get_field<2>(s); // s.hello
auto& my_struct_v3 = get_field<3>(s); // s.arr
auto& my_struct_v4 = get_field<4>(s); // s.map
// visit each field
for_each_field(s, [](std::string_view field, auto& value) {
// i: 287
// d: 3.14
// hello: Hello World
// arr: [1, 2, 3]
// map: {"one": 1, "two": 2}
std::println("{}: {}", field, value);
});
```
## API References
### Concepts
```cpp
template
concept field_countable;
template
concept field_referenceable;
template
concept field_namable;
```
The `field_countable` is a concept that checks if the type `T` is a field-countable struct. Internally, it is equivalent to that `T` is [aggregate type](https://en.cppreference.com/w/cpp/types/is_aggregate) and the number of the field is less than or equal to `100`.
The `field_referenceable` is a concept that checks if a field of the type `T` can be referenced by index. This includes the `field_countable` concept. The implementation of the `field_referenceable` concept is the condition that the `field_countable` type `T` has no base class.
The `field_namable` is a concept that checks if a field name of the type `T` can be obtained by index statically. This includes the `field_referenceable` concept and also requires that the type `T` has a field and (practically) there is no reference type member.
### `field_count`
```cpp
template
constexpr std::size_t field_count;
```
Get the number of fields from the `field_countable` type `T`.
### `field_name`
```cpp
template
constexpr std::string_view field_name;
```
Get the name of the `N`-th field as `std::string_view` from the `field_namable` type `T`.
### `field_type`
```cpp
template
using field_type;
```
Get the type of the `N`-th field from the `field_referenceable` type `T`.
### `get_field`
```cpp
// reference
template
constexpr auto& get_field(T& t) noexcept;
// const reference
template
constexpr const auto& get_field(const T& t) noexcept;
// rvalue reference
template
constexpr auto get_field(T&& t) noexcept;
```
Extracts the `N`-th element from the `field_referenceable` type `T` and returns a reference to it. It behaves like `std::get` for `std::tuple` but returns a lvalue value instead of a rvalue reference.
### `type_name`
```cpp
template
constexpr std::string_view type_name;
```
Get the name of the type `T`.
Example
```cpp
#include
#include
#include
#include // std::exchange
#include
using Token = std::variant;
struct Number {
int value;
};
struct Identifier {
std::string name;
};
template
inline constexpr bool alternative_of = false;
template
inline constexpr bool alternative_of> =
(std::is_same_v || ...);
template
requires alternative_of
struct std::formatter {
constexpr auto parse(auto& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() and *it != '}') {
throw std::format_error("invalid format");
}
return it;
}
auto format(const T& t, auto& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
out = std::format_to(out, "{} {{", field_reflection::type_name);
const char* dlm = "";
field_reflection::for_each_field(
t, [&](std::string_view name, const auto& value) {
std::format_to(
out, "{}\n .{}={}", std::exchange(dlm, ","), name, value);
});
out = std::format_to(out, "\n}}");
return out;
}
};
#include
int main() {
Number num{42};
Identifier ident{"ident"};
std::cout << std::format("{}", num) << std::endl;
std::cout << std::format("{}", ident) << std::endl;
// Expected Output
// ===============
// Number {
// .value=42
// }
// Identifier {
// .name=ident
// }
}
```
🔗[Execution example in Compiler Explorer](https://godbolt.org/z/94fPc895o)
### `for_each_field`, `all_of_field`, `any_of_field`
```cpp
// unary operation
template
void for_each_field(T&& t, Func&& func);
template
bool all_of_field(T&& t, Func&& func);
template
bool any_of_field(T&& t, Func&& func);
// binary operation
template
void for_each_field(T&& t1, T&& t2, Func&& func);
template
bool all_of_field(T&& t1, T&& t2, Func&& func);
template
bool any_of_field(T&& t1, T&& t2, Func&& func);
```
Visits each field of the type `T` and applies the unary or binary operation `func`. The `func` must be a callable object that takes one of the following kinds of arguments:
* Arguments of one or two references to the field for the `field_referenceable` type `T`.
* Arguments of `std::string_view` and one or two references to the field for the `field_namable` type `T`.
The `for_each_field` just applies the `func` and returns `void`, while the `all_of_field` and `any_of_field` return `bool` indicating whether all or any of the `func` returns `true`.
For example, the following code prints the field names and values of the `my_struct` `s`:
```cpp
constexpr auto func = [](std::string_view field, auto& value) {
std::println("{}: {}", field, value);
};
for_each_field(s, func);
```
The above is equivalent to:
```cpp
func("i"sv, s.i);
func("d"sv, s.d);
func("hello"sv, s.hello);
func("arr"sv, s.arr);
func("map"sv, s.map);
```
The first argument in the definition of the `func` can be omitted if it is not needed.
The binary operation version of `for_each_field` is useful for comparing each field of two objects of the same type:
```cpp
constexpr auto func = [](std::string_view field, auto& value1, auto& value2) {
if (value1 != value2) {
std::println("s1 and s2 have a different value: s1.{} = {}, s2.{} = {}",
field, value1, field, value2);
}
};
for_each_field(s1, s2, func);
```
### `to_tuple`
```cpp
template
constexpr std::tuple<...> to_tuple(T&& t);
```
Copy a `field_referenceable` type `T` object and convert it to `std::tuple` where each field has the same type as `T`. For example, `my_struct` object can be converted to object of type `std::tuple, std::map>`.
## Acknowledgments
This project is strongly inspired by the following and stands as
* an alternative to [visit_struct](https://github.com/cbeck88/visit_struct) without macros,
* a reflection library that is a partial reimplementation of [reflect-cpp](https://github.com/getml/reflect-cpp).
The C++20 implementation of the counting field in this library is partially referenced to [Boost.PFR](https://github.com/boostorg/pfr).