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