v0.3.0 API Reference
Enum.h
1//
2// Enum.h
3// avara3d
4//
5// Created by Morgan Davis on 8/17/26.
6// Copyright © 2026 Morgan K Davis. All rights reserved.
7//
8
9#ifndef AVARA3D_UTIL_ENUM_H
10#define AVARA3D_UTIL_ENUM_H
11
12#include <array>
13#include <cstddef>
14#include <optional>
15#include <string_view>
16#include <type_traits>
17#include <utility>
18
19namespace a3d::util::enums {
20namespace detail {
21
22 // matches magic_enum's default reflection range.
23 static constexpr int ENUM_RANGE_MIN = -128;
24 static constexpr int ENUM_RANGE_MAX = 127;
25
26 template<typename E>
27 constexpr int range_min() noexcept {
28 if constexpr (std::is_signed_v<std::underlying_type_t<E>>) {
29 return ENUM_RANGE_MIN;
30 }
31 else {
32 return 0;
33 }
34 }
35
36 template<typename E>
37 constexpr int range_max() noexcept {
38 return ENUM_RANGE_MAX;
39 }
40
41 constexpr std::string_view unqualified_name(std::string_view name) noexcept {
42
43 const auto scope = name.rfind("::");
44 if (scope != std::string_view::npos) {
45 name.remove_prefix(scope + 2);
46 }
47
48 return name;
49 }
50
51 template<auto V>
52 consteval std::string_view enum_name_impl() noexcept {
53
54 using E = decltype(V);
55 static_assert(std::is_enum_v<E>);
56
57#if defined(__clang__) || defined(__GNUC__)
58
59 constexpr std::string_view signature = __PRETTY_FUNCTION__;
60 constexpr std::string_view prefix = "V = ";
61
62 const auto prefixPos = signature.find(prefix);
63 if (prefixPos == std::string_view::npos) {
64 return {};
65 }
66
67 const auto begin = prefixPos + prefix.size();
68
69 #if defined(__clang__)
70 const auto end = signature.find(']', begin);
71 #else
72 auto end = signature.find(';', begin);
73 if (end == std::string_view::npos) {
74 end = signature.find(']', begin);
75 }
76 #endif
77
78 if (end == std::string_view::npos) {
79 return {};
80 }
81
82 auto name = signature.substr(begin, end - begin);
83
84#elif defined(_MSC_VER)
85
86 constexpr std::string_view signature = __FUNCSIG__;
87 constexpr std::string_view prefix = "enum_name_impl<";
88
89 const auto prefixPos = signature.find(prefix);
90 if (prefixPos == std::string_view::npos) {
91 return {};
92 }
93
94 const auto begin = prefixPos + prefix.size();
95 const auto end = signature.find(">(void)", begin);
96 if (end == std::string_view::npos) {
97 return {};
98 }
99
100 auto name = signature.substr(begin, end - begin);
101
102#else
103 #error Unsupported compiler for a3d::util::enums
104#endif
105
106 // unnamed enum values are rendered by the supported compilers as
107 // casts or integer values rather than symbolic enumerator names.
108 if (name.empty() || name.front() == '(' || name.front() == '-'
109 || (name.front() >= '0' && name.front() <= '9')) {
110 return {};
111 }
112
113 return unqualified_name(name);
114 }
115
116 template<typename E, int Min, std::size_t... I>
117 consteval auto make_names(std::index_sequence<I...>) noexcept {
118
119 return std::array<std::string_view, sizeof...(I)> {
120 enum_name_impl<static_cast<E>(Min + static_cast<int>(I))>()...};
121 }
122
123 template<typename E>
124 struct EnumData {
125 static_assert(std::is_enum_v<E>);
126
127 static constexpr int min = range_min<E>();
128 static constexpr int max = range_max<E>();
129 static constexpr std::size_t count = static_cast<std::size_t>(max - min + 1);
130
131 static constexpr auto names = make_names<E, min>(std::make_index_sequence<count> {});
132 };
133
134} // namespace detail
135
136// [Public Functions]
137
143template<typename E>
144constexpr std::string_view enum_name(E value) noexcept {
145
146 static_assert(std::is_enum_v<E>, "a3d::util::enums::enum_name() requires an enum type");
147
148 using U = std::underlying_type_t<E>;
149
150 constexpr int min = detail::EnumData<E>::min;
151 constexpr int max = detail::EnumData<E>::max;
152 constexpr auto& names = detail::EnumData<E>::names;
153
154 const U raw = static_cast<U>(value);
155 if (raw < static_cast<U>(min) || raw > static_cast<U>(max)) {
156 return {};
157 }
158
159 return names[static_cast<std::size_t>(raw - static_cast<U>(min))];
160}
161
167template<typename E>
168constexpr std::optional<E> enum_cast(std::string_view name) noexcept {
169
170 static_assert(std::is_enum_v<E>, "a3d::util::enums::enum_cast() requires an enum type");
171
172 constexpr int min = detail::EnumData<E>::min;
173 constexpr auto& names = detail::EnumData<E>::names;
174
175 for (std::size_t i = 0; i < names.size(); ++i) {
176 if (!names[i].empty() && names[i] == name) {
177 return static_cast<E>(min + static_cast<int>(i));
178 }
179 }
180
181 return {};
182}
183
185template<typename E>
186constexpr std::underlying_type_t<E> to_underlying(E value) noexcept {
187
188 static_assert(std::is_enum_v<E>, "a3d::util::enums::to_underlying() requires an enum type");
189 return static_cast<std::underlying_type_t<E>>(value);
190}
191
192} // namespace a3d::util::enums
193
194#endif // AVARA3D_UTIL_ENUM_H