ThreadSchedule 2.2.0
Modern C++ thread management library
Loading...
Searching...
No Matches
reflection.hpp
Go to the documentation of this file.
1#pragma once
2
3#if defined(THREADSCHEDULE_HAS_REFLECTION) && THREADSCHEDULE_HAS_REFLECTION
4
5#include <cstddef>
6#include <array>
7#include <meta>
8#include <span>
9#include <string_view>
10#include <tuple>
11#include <type_traits>
12#include <utility>
13
14namespace threadschedule::reflect
15{
16
17using info = std::meta::info;
18inline constexpr bool enabled = true;
19
20template <typename T>
21consteval auto fields() -> std::span<info const>
22{
23 static_assert(std::meta::is_class_type(^^T) || std::meta::is_union_type(^^T),
24 "threadschedule::reflect::fields<T>() requires a class or union type");
25 return std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::current()));
26}
27
28template <typename T>
29consteval auto field_count() -> std::size_t
30{
31 return fields<T>().size();
32}
33
34template <typename T, std::size_t I>
35consteval auto field_info() -> info
36{
37 static_assert(I < field_count<T>(), "reflection field index out of range");
38 return fields<T>()[I];
39}
40
41template <info Field>
42consteval auto field_name() -> std::string_view
43{
44 return std::string_view(std::define_static_string(std::meta::identifier_of(Field)));
45}
46
47template <typename T, std::size_t I>
48consteval auto field_name() -> std::string_view
49{
50 return field_name<field_info<T, I>()>();
51}
52
53template <typename T>
54consteval auto type_name() -> std::string_view
55{
56 return std::string_view(std::define_static_string(std::meta::display_string_of(^^T)));
57}
58
59template <info Field>
60using field_type_t = [: std::meta::type_of(Field) :];
61
62template <info Field, typename T>
63constexpr decltype(auto) get(T&& obj)
64{
65 return (std::forward<T>(obj).[:Field:]);
66}
67
68template <info Field, typename Owner>
69inline constexpr bool is_field_of_v = std::meta::is_same_type(std::meta::parent_of(Field), ^^Owner);
70
71template <typename T>
72consteval auto field_names() -> std::span<char const* const>;
73
74namespace detail
75{
76
77template <info... Fields>
78struct projection_type;
79
80template <info Field>
81struct projection_type<Field>
82{
83 using type = field_type_t<Field>;
84};
85
86template <info First, info Second, info... Rest>
87struct projection_type<First, Second, Rest...>
88{
89 using type = std::tuple<field_type_t<First>, field_type_t<Second>, field_type_t<Rest>...>;
90};
91
92template <typename T, typename F, std::size_t... I>
93constexpr void visit_fields_impl(T&& obj, F&& fn, std::index_sequence<I...>)
94{
95 using object_type = std::remove_cv_t<std::remove_reference_t<T>>;
96 constexpr auto names = field_names<object_type>();
97 (fn(names[I], get<field_info<object_type, I>()>(std::forward<T>(obj))), ...);
98}
99
100template <typename T, std::size_t... I>
101consteval auto field_names_impl(std::index_sequence<I...>) -> std::span<char const* const>
102{
103 return std::define_static_array(
104 std::array<char const*, sizeof...(I)>{std::define_static_string(std::meta::identifier_of(field_info<T, I>()))...});
105}
106
107template <info First>
108consteval info first_field() noexcept
109{
110 return First;
111}
112
113template <info Field, typename Owner>
114consteval void require_field_owner()
115{
116 static_assert(std::meta::is_same_type(std::meta::parent_of(Field), ^^Owner),
117 "Reflection field does not belong to the requested owner type");
118}
119
120} // namespace detail
121
122template <typename T>
123consteval auto field_names() -> std::span<char const* const>
124{
125 return detail::field_names_impl<T>(std::make_index_sequence<field_count<T>()>{});
126}
127
128template <typename T, typename F>
129constexpr void visit_fields(T&& obj, F&& fn)
130{
131 using object_type = std::remove_cv_t<std::remove_reference_t<T>>;
132 detail::visit_fields_impl(std::forward<T>(obj), std::forward<F>(fn),
133 std::make_index_sequence<field_count<object_type>()>{});
134}
135
136template <info... Fields>
137using projection_t = typename detail::projection_type<Fields...>::type;
138
139template <info... Fields, typename T>
140constexpr auto project_value(T&& obj) -> projection_t<Fields...>
141{
142 static_assert(sizeof...(Fields) > 0, "project_value requires at least one field");
143 if constexpr (sizeof...(Fields) == 1)
144 {
145 return get<detail::first_field<Fields...>()>(std::forward<T>(obj));
146 }
147 else
148 {
149 return projection_t<Fields...>{get<Fields>(std::forward<T>(obj))...};
150 }
151}
152
153template <info Field, typename Owner>
154consteval void require_field_owner()
155{
156 detail::require_field_owner<Field, Owner>();
157}
158
159} // namespace threadschedule::reflect
160
161#endif