GMP 0.3.0
Generative Metaprogramming library for C++
Loading...
Searching...
No Matches
meta.hpp
Go to the documentation of this file.
1// ___ __ __ ___
2// / __| \/ | _ \ GMP(Generative Metaprogramming)
3// | (_ | |\/| | _/ version 0.3.0
4// \___|_| |_|_| https://github.com/lkimuk/gmp
5//
6// SPDX-FileCopyrightText: 2026 Miles Li <https://www.cppmore.com/>
7// SPDX-License-Identifier: MIT
8//
9// This file is part of the GMP (Generative Metaprogramming) library.
10// Full project source: https://github.com/lkimuk/gmp
11
12#ifndef GMP_META_HPP_
13#define GMP_META_HPP_
14
15#include <optional>
16#include <source_location>
17
19#include <gmp/meta/detail/member_ref.hpp>
20
21namespace gmp {
22
38template<typename E>
40 static constexpr int min = -128;
41 static constexpr int max = 127;
42 // static constexpr bool is_flags = false;
43 // static constexpr bool allow_alias = false;
44};
45
57#define GMP_ENUM_RANGE(Enum, Min, Max) \
58 template<> \
59 struct gmp::enum_traits<Enum> { \
60 static constexpr int min = Min; \
61 static constexpr int max = Max; \
62 }
63
74#define GMP_ENUM_VALUES(Enum, ...) \
75 template<> \
76 struct gmp::enum_traits<Enum> { \
77 static constexpr auto values = std::to_array({__VA_ARGS__}); \
78 }
79
85template<typename E>
86inline constexpr int enum_min_v = enum_traits<E>::min;
87
93template<typename E>
94inline constexpr int enum_max_v = enum_traits<E>::max;
95
98namespace detail {
99
100template<typename E>
101concept has_enum_values =
102 requires {
104 std::tuple_size<std::remove_cvref_t<decltype(enum_traits<E>::values)>>::value;
105 } &&
106 std::same_as<
107 typename std::remove_cvref_t<decltype(enum_traits<E>::values)>::value_type,
108 E
109 >;
110
111template<typename E, fixed_string P, auto V>
112consteval bool is_valid_enum_value() {
113 constexpr auto name = value_name_of<static_cast<E>(V)>();
114 return name.find(P.data()) != std::string_view::npos;
115}
116
117template<typename E, fixed_string P, int Min, std::size_t... I>
118consteval auto enum_values_scan(std::index_sequence<I...>) {
119 constexpr std::size_t count = (std::size_t{0} + ... + (is_valid_enum_value<E, P, Min + static_cast<int>(I)>() ? 1u : 0u));
120
121 std::array<E, count> result{};
122 std::size_t index = 0;
123
124 auto append = [&]<int V>() consteval {
125 if constexpr (is_valid_enum_value<E, P, V>()) {
126 result[index++] = static_cast<E>(V);
127 }
128 };
129
130 (append.template operator()<Min + static_cast<int>(I)>(), ...);
131 return result;
132}
133
134} // namespace detail
135
155consteval auto enum_values() {
156 static_assert(std::is_enum_v<E>, "enum_values<E>() requires E to be an enum type");
157
158 if constexpr (detail::has_enum_values<E>) {
160 } else {
161 constexpr int min = enum_min_v<E>;
162 constexpr int max = enum_max_v<E>;
163
164 static_assert(min <= max, "enum_traits<E>::min must be <= max");
165
166 return detail::enum_values_scan<E, P, min>(
167 std::make_index_sequence<static_cast<std::size_t>(max - min + 1)>{}
168 );
169 }
170}
171
201consteval auto enum_count() {
202 static_assert(std::is_enum_v<E>, "enum_count<E>() requires E to be an enum type");
203
204 return enum_values<E, P>().size();
205}
206
234template<auto E, fixed_string P = fixed_string("::")>
235consteval auto enum_name() {
236 constexpr auto name = detail::value_name_of<E>();
237 constexpr auto start = name.rfind(P.data());
238 if constexpr (start != std::string_view::npos) {
239#if GMP_COMPILER_CLANG || GMP_COMPILER_GCC
240 constexpr auto end = name.find_last_of("]");
241#else
242 constexpr auto end = name.find_last_of(">");
243#endif
244 return name.substr(start + P.size(), end - start - P.size());
245 } else {
246 return "<unnamed>";
247 }
248}
249
290template<typename E>
291consteval auto enum_names() {
292 constexpr auto values = enum_values<E>();
293 constexpr std::size_t size = values.size();
294
295 if constexpr (size == 0) {
296 return std::array<std::string_view, 0>{};
297 } else {
298 return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
299 return std::array<std::string_view, size>{
301 };
302 }(std::make_index_sequence<size>{});
303 }
304}
305
316template<typename E>
317consteval auto enum_entries() {
318 constexpr auto values = enum_values<E>();
319 constexpr auto names = enum_names<E>();
320 constexpr std::size_t size = values.size();
321
322 if constexpr (size == 0) {
323 return std::array<std::pair<E, std::string_view>, 0>{};
324 } else {
325 return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
326 return std::array<std::pair<E, std::string_view>, size>{
327 std::pair<E, std::string_view>{ values[Is], names[Is] }...
328 };
329 }(std::make_index_sequence<size>{});
330 }
331}
332
341template<typename E>
342constexpr auto enum_index(E value) -> std::optional<std::size_t> {
343 constexpr auto values = enum_values<E>();
344 for (std::size_t i = 0; i < values.size(); ++i) {
345 if (values[i] == value) {
346 return i;
347 }
348 }
349 return std::nullopt;
350}
351
360template<typename E>
361constexpr auto enum_cast(std::string_view name) -> std::optional<E> {
362 constexpr auto entries = enum_entries<E>();
363 for (const auto& [value, n] : entries) {
364 if (n == name) {
365 return value;
366 }
367 }
368 return std::nullopt;
369}
370
400template<typename T, typename... Args>
401 requires std::is_aggregate_v<std::remove_cvref_t<T>>
402consteval int member_count() {
403 using value_type = std::remove_cvref_t<T>;
404 if constexpr (requires { value_type{Args{}...}; }) {
405 return member_count<value_type, Args..., gmp::any>();
406 } else {
407 return sizeof...(Args) - 1;
408 }
409}
410
463template<std::size_t I, typename T>
464 requires std::is_aggregate_v<T> &&
467consteval auto member_name() noexcept {
468 constexpr auto name = detail::member_name_of<detail::field_getter<I, T>(constant_arg<member_count<T>()>)>();
469 return name;
470}
471
473template<std::size_t I, typename T>
474consteval auto member_name() noexcept {
475 static_assert(std::is_aggregate_v<T>, "member_name() can only be used with aggregate types.");
476 static_assert(I < member_count<T>(), "Index out of bounds in member_name().");
477 static_assert(member_count<T>() <= GMP_MAX_SUPPORTED_FIELDS, "member_name() only supports up to " GMP_STRINGIFY(GMP_MAX_SUPPORTED_FIELDS) " fields.");
478}
537template<typename T>
538consteval auto member_names() {
539 using value_type = std::remove_cvref_t<T>;
540 constexpr auto size = member_count<value_type>();
541 if constexpr (size == 0) {
542 return std::array<std::string_view, 0>{};
543 } else {
544 return []<std::size_t... Is>(std::index_sequence<Is...>) {
545 return (std::array<std::string_view, size> {
547 });
548 }(std::make_index_sequence<size>{});
549 }
550}
551
567template<std::size_t I, typename T>
568using member_type_t = std::remove_cvref_t<
569 decltype(*detail::field_getter<I, T>(constant_arg<member_count<T>()>))>;
570
573namespace detail {
574
575template<typename T>
576struct member_type_names_holder {
577 static constexpr std::size_t N = member_count<T>();
578
579 static constexpr auto fixed_names = []<std::size_t... Is>(std::index_sequence<Is...>) {
580 return std::tuple{ pretty_type_name<member_type_t<Is, T>>()()... };
581 }(std::make_index_sequence<N>{});
582
583 static constexpr auto views = []<std::size_t... Is>(std::index_sequence<Is...>) {
584 return std::array<std::string_view, N>{
585 std::get<Is>(fixed_names).to_string_view()...
586 };
587 }(std::make_index_sequence<N>{});
588};
589
590} // namespace detail
591
624template<typename T>
625constexpr auto member_type_names() {
626 return detail::member_type_names_holder<T>::views;
627}
628
641template<std::size_t I, typename T, typename UnqualifiedT = std::remove_cvref_t<T>>
642 requires std::is_aggregate_v<UnqualifiedT>
645decltype(auto) member_ref(T&& value) noexcept {
646 return detail::member_ref<I, T>(value, constant_arg<member_count<UnqualifiedT>()>);
647}
648
650template<std::size_t I, typename T, typename UnqualifiedT = std::remove_cvref_t<T>>
651decltype(auto) member_ref(T&&) noexcept {
652 static_assert(std::is_aggregate_v<UnqualifiedT>, "member_ref() can only be used with aggregate types.");
653 static_assert(I < member_count<UnqualifiedT>(), "Index out of bounds in member_ref().");
654 static_assert(member_count<UnqualifiedT>() <= GMP_MAX_SUPPORTED_FIELDS, "member_ref() only supports up to " GMP_STRINGIFY(GMP_MAX_SUPPORTED_FIELDS) " fields.");
655}
658namespace detail {
659
660#define GMP_FOR_EACH_MEMBER_DEFINE(N) \
661 template<typename T, typename F> \
662 void for_each_member_impl(T&& value, F&& f, constant_arg_t<N>) noexcept { \
663 auto&& [GMP_GET_FIRST_N(N, GMP_IDENTIFIERS)] = value; \
664 auto members = std::forward_as_tuple(GMP_GET_FIRST_N(N, GMP_IDENTIFIERS)); \
665 constexpr auto mem_names = gmp::member_names<T>(); \
666 std::size_t index = 0; \
667 std::apply( \
668 [&](auto&&... member) { (f(mem_names[index++], std::forward<decltype(member)>(member)), ...); }, members); \
669 }
670
671#if GMP_STANDARD_PREPROCESSOR
672 // Standard preprocessor supports 256 arguments
674#else
675 // MSVC traditional preprocessor: MAX 199 due to nesting depth limit (fatal error C1009)
677#endif
678
679#undef GMP_FOR_EACH_MEMBER_DEFINE
680
681} // namespace detail
682
695template<typename T, typename F>
696 requires std::is_aggregate_v<std::remove_cvref_t<T>>
697void for_each_member(T&& value, F&& func) noexcept {
698 detail::for_each_member_impl(
699 std::forward<T>(value),
700 std::forward<F>(func),
702 );
703}
704
706template<typename T, typename F>
707void for_each_member(T&& value, F&& func) noexcept {
708 static_assert(std::is_aggregate_v<std::remove_cvref_t<T>>,
709 "for_each_member() can only be used with aggregate types.");
710}
715} // namespace gmp
716
717#endif // GMP_META_HPP_
#define GMP_FOR_EACH(call,...)
Call a macro for each argument provided.
Definition macro.hpp:1290
#define GMP_RANGE(begin, end)
Macro to generate a sequence of numbers from begin to end.
Definition macro.hpp:4827
#define GMP_STRINGIFY(x)
Convert a macro argument to a string literal.
Definition macro.hpp:132
consteval int member_count()
Count the number of members in an aggregate type at compile-time.
Definition meta.hpp:402
consteval auto enum_names()
Get all enumerator names of an enumeration type at compile-time.
Definition meta.hpp:291
consteval auto enum_count()
Count the number of enumerators in an enumeration type at compile-time.
Definition meta.hpp:201
consteval auto enum_name()
Get the name of an enumerator at compile-time.
Definition meta.hpp:235
void for_each_member(T &&value, F &&func) noexcept
Visit each member of an aggregate object together with its member name.
Definition meta.hpp:697
constexpr auto member_type_names()
Returns an array of string_view containing the type names of all members of aggregate type T.
Definition meta.hpp:625
constexpr int enum_max_v
The maximum reflection scan value for enumeration type E.
Definition meta.hpp:94
std::remove_cvref_t< decltype(*detail::field_getter< I, T >(constant_arg< member_count< T >()>))> member_type_t
Alias for the type of the I-th member of an aggregate type.
Definition meta.hpp:569
decltype(auto) member_ref(T &&value) noexcept
Get a reference to the I-th member of an aggregate object.
Definition meta.hpp:645
constexpr int enum_min_v
The minimum reflection scan value for enumeration type E.
Definition meta.hpp:86
consteval auto member_names()
Get all member names of an aggregate type at compile-time.
Definition meta.hpp:538
consteval auto enum_entries()
Get all enumerator entries (value, name) of an enumeration type at compile-time.
Definition meta.hpp:317
consteval auto enum_values()
Get all enumerator values of an enumeration type at compile-time.
Definition meta.hpp:155
constexpr auto enum_index(E value) -> std::optional< std::size_t >
Get the index of an enumerator value in enum_values<E>().
Definition meta.hpp:342
consteval auto member_name() noexcept
Get the name of a specific member of an aggregate type at compile-time.
Definition meta.hpp:467
constexpr auto enum_cast(std::string_view name) -> std::optional< E >
Cast an enumerator name to its corresponding enumerator value.
Definition meta.hpp:361
constexpr constant_arg_t< V > constant_arg
A ready-to-use constant_arg_t<V> object for a compile-time value.
Definition utility.hpp:40
#define GMP_FOR_EACH_MEMBER_DEFINE(N)
Definition meta.hpp:660
Definition lock.hpp:21
A placeholder type implicitly convertible to any type.
Definition utility.hpp:48
Customize the reflection range or explicit values for an enumeration.
Definition meta.hpp:39
static constexpr int max
Definition meta.hpp:41
static constexpr int min
Definition meta.hpp:40
A compile-time string type with fixed length and constexpr operations.
Format a type name for documentation-friendly display.
Definition type_name.hpp:72