enum_name
Loading...
Searching...
No Matches
enum_name_impl.hpp
1/*
2MIT License
3
4Copyright (c) 2024 mguludag
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24
25#ifndef MGUTILITY_REFLECTION_DETAIL_ENUM_NAME_IMPL_HPP
26#define MGUTILITY_REFLECTION_DETAIL_ENUM_NAME_IMPL_HPP
27
28// NOLINTNEXTLINE [unused-includes]
29#include "enum_for_each.hpp"
30#include "meta.hpp"
31#include "mgutility/_common/definitions.hpp"
32#include "mgutility/std/fixed_string.hpp"
33#include "mgutility/std/optional.hpp"
34#include "mgutility/std/string_view.hpp"
35#include "mgutility/std/utility.hpp"
36
37#include <algorithm>
38#include <array>
39#include <cstddef>
40
46#if defined(_MSC_VER) && _MSC_VER < 1910
47#error "Requires MSVC 2017 or newer!"
53#elif defined(__clang__) && __clang_major__ < 6
54#error "Requires clang 6 or newer!"
60#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9
61#error "Requires gcc 9 or newer!"
67#elif !defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
68#error "Your compiler is not supported!"
69#endif
70
76#ifdef _MSC_VER
77#define __PRETTY_FUNCTION__ __FUNCSIG__
78#endif
79
80// NOLINTNEXTLINE [modernize-concat-nested-namespaces]
81namespace mgutility {
82namespace detail {
83
84#ifndef MGUTILITY_STRLEN
85// NOLINTNEXTLINE [cppcoreguidelines-macro-usage]
86#define MGUTILITY_STRLEN(x) sizeof(x) - 1
87#endif
88
89template <typename T, int Min, int Max> struct enum_name_parse_result {
90 static constexpr auto size = std::size_t{Max - Min};
92 std::array<pair<std::size_t, std::size_t>, size> ranges;
93};
94
95template <typename T, int Min, int Max>
96using enum_name_array =
97 std::array<mgutility::string_view, static_cast<std::size_t>(Max - Min)>;
98
103struct enum_type {
104private:
112 template <typename Enum, Enum... e>
113 MGUTILITY_CNSTXPR static mgutility::string_view
114 raw_name(detail::enum_sequence<Enum, e...> /*unused*/) noexcept {
115#if defined(__GNUC__) && !defined(__clang__) && MGUTILITY_CPLUSPLUS >= 201402L
116#define PREFIX \
117 MGUTILITY_STRLEN("static constexpr mgutility::string_view " \
118 "mgutility::detail::enum_type::raw_name(mgutility::detail:" \
119 ":enum_sequence<Enum, e ...>) [with Enum = ")
120#elif defined(__clang__) || defined(__GNUC__)
121#define PREFIX \
122 MGUTILITY_STRLEN("static mgutility::string_view " \
123 "mgutility::detail::enum_type::raw_name(mgutility::detail:" \
124 ":enum_sequence<Enum, e ...>) [with Enum = ")
125#elif defined(_MSC_VER)
126#if MGUTILITY_CPLUSPLUS > 201402L
127#define PREFIX \
128 MGUTILITY_STRLEN( \
129 "class std::basic_string_view<char,struct std::char_traits<char> > " \
130 "__cdecl mgutility::detail::enum_type::raw_name")
131#else
132#define PREFIX \
133 MGUTILITY_STRLEN("class mgutility::basic_string_view<char> __cdecl " \
134 "mgutility::detail::enum_type::raw_name")
135#endif
136#else
137#define PREFIX 0
138#endif
139
140 return mgutility::string_view(__PRETTY_FUNCTION__ + PREFIX,
141 MGUTILITY_STRLEN(__PRETTY_FUNCTION__) -
142 PREFIX + 1);
143 }
144
151 template <typename Enum, int Min, int Max>
152 MGUTILITY_CNSTXPR static enum_name_parse_result<Enum, Min, Max>
153 parse() noexcept {
154 MGUTILITY_CNSTXPR auto str =
155 raw_name<Enum>(detail::make_enum_sequence<Enum, Min, Max>{});
156
157#if defined(__clang__) || defined(__GNUC__)
158#if defined(__clang__)
159 auto end = str.rfind(']');
160#elif defined(__GNUC__) && !defined(__clang__)
161 auto end = str.rfind(';');
162#endif
163
164 auto enum_names = str.substr(0, end);
165
166#elif defined(_MSC_VER)
167 // MSVC: different format
168 auto pos = str.find(',');
169 if (pos == mgutility::string_view::npos)
170 return {};
171
172 ++pos;
173
174 auto end = str.rfind('>');
175 auto enum_names = str.substr(pos, end - pos);
176
177#else
178 return {};
179#endif
180
181 if (enum_names.empty()) {
182 return {};
183 }
184
186
187 std::size_t idx = 0;
188
189 while (!enum_names.empty() && idx < result.ranges.size()) {
190 auto pos = enum_names.find(',');
191 if (pos != mgutility::string_view::npos) {
192 auto token = enum_names.substr(0, pos);
193
194 // remove whitespace
195 while (!token.empty() && token.front() == ' ') {
196 token = token.substr(1);
197 }
198
199 while (!token.empty() && token.back() == ' ') {
200 token = token.substr(0, token.size() - 1);
201 }
202
203 std::size_t begin = token.find('(');
204 std::size_t end = token.rfind(')');
205 if (begin != mgutility::string_view::npos ||
206 end != mgutility::string_view::npos) {
207 result.ranges[idx++] = {0, 0};
208 enum_names = enum_names.substr(pos + 1);
209 continue;
210 }
211
212 std::size_t start = 0;
213 if (token.find(':') != mgutility::string_view::npos) {
214 start = token.find(':') + 2;
215 token = token.substr(start);
216 }
217
218 auto size = result.blob.size();
219 result.blob.append(token);
220 result.ranges[idx++] = {size, result.blob.size() - size};
221
222 enum_names = enum_names.substr(pos + 1);
223 continue;
224 }
225 enum_names = {};
226 }
227
228 return result;
229 }
230
231public:
239 template <typename Enum, int Min, int Max>
240 MGUTILITY_CNSTXPR static enum_name_parse_result<Enum, Min, Max>
241 name() noexcept {
242 return parse<Enum, Min, Max>();
243 }
244};
245
252template <typename Enum, int Min, int Max> struct enum_array_cache;
253
260template <typename Enum, int Min, int Max> struct enum_array_cache {
261
262#if MGUTILITY_CPLUSPLUS > 201402L
263
264 static inline constexpr auto parse_result =
265 enum_type::template name<Enum, Min, Max>();
266
267#else
268 // C++11: lazy runtime array
271 enum_type::template name<Enum, Min, Max>();
272
273 return arr;
274 }
275#endif
276
277 static MGUTILITY_CNSTXPR auto
278 apply_custom(const enum_name_parse_result<Enum, Min, Max> &result) noexcept
279 -> enum_name_array<Enum, Min, Max> {
280 enum_name_array<Enum, Min, Max> arr{};
281
282 for (std::size_t idx = 0; idx < result.ranges.size(); ++idx) {
283 arr[idx] = result.blob.view().substr(result.ranges[idx].first,
284 result.ranges[idx].second);
285 }
286
287#if MGUTILITY_CPLUSPLUS >= 201402L
288 constexpr auto map = mgutility::custom_enum<Enum>::map;
289 for (const auto &pair : map) {
290#else
291 for (const auto &pair : mgutility::custom_enum<Enum>::map) {
292#endif
293 if (pair.first >= static_cast<Enum>(Min) &&
294 pair.first < static_cast<Enum>(Max)) {
295 arr[static_cast<std::size_t>(pair.first) - Min] = pair.second;
296 }
297 }
298
299 return arr;
300 }
301};
302
312template <typename Enum, int Min = mgutility::enum_range<Enum>::min,
314MGUTILITY_CNSTXPR auto get_enum_array() noexcept
315 -> enum_name_array<Enum, Min, Max> {
316#if MGUTILITY_CPLUSPLUS > 201402L
319#else
322#endif
323}
324
334template <typename Enum, int Min, int Max>
335MGUTILITY_CNSTXPR inline auto to_enum_impl(mgutility::string_view str) noexcept
337 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
338
339 const auto index{detail::find(arr, str)};
340 return index == 0 ? mgutility::nullopt
341 : mgutility::optional<Enum>{static_cast<Enum>(index + Min)};
342}
343
353template <typename Enum, int Min, int Max>
354MGUTILITY_CNSTXPR auto to_enum_bitmask_impl(mgutility::string_view str) noexcept
356
357 // Check if the string contains a '|' character
358 if (str.find('|') == mgutility::string_view::npos) {
359 return to_enum_impl<Enum, Min, Max>(str);
360 }
361
363 std::size_t index = 0;
364
365 for (std::size_t i = 0; i < str.size(); ++i) {
366 if (str[i] == '|') {
367 auto name = str.substr(index, i - index);
368 auto maybe_enum = to_enum_impl<Enum, Min, Max>(name);
369
370 if (!name.empty() && maybe_enum) {
371 result.emplace(result ? static_cast<Enum>(*result | *maybe_enum)
372 : *maybe_enum);
373 }
374
375 index = i + 1;
376 }
377 }
378
379 auto maybe_enum = to_enum_impl<Enum, Min, Max>(str.substr(index));
380 if (result && maybe_enum) {
381 result.emplace(static_cast<Enum>(*result | *maybe_enum));
382 } else {
383 result.reset();
384 }
385
386 return result;
387}
388
398template <typename Enum, int Min, int Max,
399 detail::enable_if_t<!detail::has_bit_or<Enum>::value, bool> = true>
400MGUTILITY_CNSTXPR auto enum_name_impl(Enum enumValue) noexcept
402 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
403 const auto index{(Min < 0 ? -Min : Min) + static_cast<int>(enumValue)};
404 if (index < Min || index > static_cast<int>(arr.size()) - 1) {
405 return mgutility::string_view{};
406 }
407
408 return arr[static_cast<size_t>(index)];
409}
410
421// NOLINTNEXTLINE [modernize-use-constraints]
422template <typename Enum, int Min, int Max,
423 detail::enable_if_t<detail::has_bit_or<Enum>::value, bool> = true>
424MGUTILITY_CNSTXPR_CLANG_WA auto enum_name_impl(Enum enumValue) noexcept
426
427 // Get the array of enum names
428 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
429
430 // Calculate the index in the array
431 const auto index = (Min < 0 ? -Min : Min) + static_cast<int>(enumValue);
432
434
435 if (index >= 0 && index < static_cast<int>(arr.size())) {
436 bitmasked_name.append(arr[static_cast<size_t>(index)]);
437 }
438
439 if (!bitmasked_name.empty()) {
440 return bitmasked_name;
441 }
442
443 for (auto i = 0; i < Max - Min; ++i) {
444 if (i >= 0 && i < static_cast<int>(arr.size()) && arr[i].size() > 0 &&
445 (enumValue & static_cast<Enum>(i)) == static_cast<Enum>(i)) {
446 bitmasked_name.append(arr[i]).append("|");
447 }
448 }
449
450 // Remove the trailing '|' if present
451 if (!bitmasked_name.empty()) {
452 bitmasked_name.pop_back();
453 }
454
455 return bitmasked_name;
456}
457} // namespace detail
458} // namespace mgutility
459
460#endif // MGUTILITY_REFLECTION_DETAIL_ENUM_NAME_IMPL_HPP
A basic string view class template.
Definition mgutility_enum_name.hpp:462
constexpr size_t size() const noexcept
Returns the size of the string.
Definition mgutility_enum_name.hpp:573
constexpr size_t find(Char c, size_t pos=0) const noexcept
Finds the first occurrence of a character.
Definition mgutility_enum_name.hpp:620
constexpr basic_string_view< Char > substr(size_t begin, size_t len=0U) const noexcept
Returns a substring view.
Definition mgutility_enum_name.hpp:589
Definition mgutility_enum_name.hpp:736
A class template that provides optional (nullable) objects.
Definition mgutility_enum_name.hpp:932
Checks for MSVC compiler version.
Definition enum_for_each.hpp:35
auto nullopt
A global instance of nullopt_t to represent null optional.
Definition mgutility_enum_name.hpp:1190
Provides the custom names map for an enumeration type.
Definition meta.hpp:227
Caches an array of enum names for a given enum sequence.
Definition enum_name_impl.hpp:260
Definition enum_name_impl.hpp:89
Represents a sequence of enumeration values.
Definition meta.hpp:156
Provides functionality to extract and parse enum names at compile-time.
Definition enum_name_impl.hpp:103
static MGUTILITY_CNSTXPR enum_name_parse_result< Enum, Min, Max > name() noexcept
Gets the name of the enum value, checking custom names first.
Definition enum_name_impl.hpp:241
Provides the range for an enumeration type.
Definition meta.hpp:204
Definition meta.hpp:209