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#include <cstring>
41
47#if defined(_MSC_VER) && _MSC_VER < 1910
48#error "Requires MSVC 2017 or newer!"
54#elif defined(__clang__) && __clang_major__ < 6
55#error "Requires clang 6 or newer!"
61#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 9
62#error "Requires gcc 9 or newer!"
68#elif !defined(_MSC_VER) && !defined(__clang__) && !defined(__GNUC__)
69#error "Your compiler is not supported!"
70#endif
71
77#ifdef _MSC_VER
78#define __PRETTY_FUNCTION__ __FUNCSIG__
79#endif
80
81// NOLINTNEXTLINE [modernize-concat-nested-namespaces]
82namespace mgutility {
83namespace detail {
84
85#ifndef MGUTILITY_STRLEN
86// NOLINTNEXTLINE [cppcoreguidelines-macro-usage]
87#define MGUTILITY_STRLEN(x) sizeof(x) - 1
88#endif
89
103template <typename U, int Min, int Max> struct enum_name_parse_result {
104 static constexpr auto size = std::size_t{Max - Min};
106 std::array<pair<std::size_t, std::size_t>, size> ranges;
107};
108
109template <typename T, int Min, int Max>
110using enum_name_array =
111 std::array<mgutility::string_view, static_cast<std::size_t>(Max - Min)>;
112
117struct enum_type {
118private:
126 template <typename Enum, Enum... e>
127 MGUTILITY_CNSTXPR static auto
128 raw_name(detail::enum_sequence<Enum, e...> /*unused*/) noexcept
130#if defined(__GNUC__) && !defined(__clang__) && MGUTILITY_CPLUSPLUS >= 201402L
131#define PREFIX \
132 MGUTILITY_STRLEN("static constexpr mgutility::string_view " \
133 "mgutility::detail::enum_type::raw_name(mgutility::detail:" \
134 ":enum_sequence<Enum, e ...>) [with Enum = ")
135#elif defined(__clang__) || defined(__GNUC__)
136#define PREFIX \
137 MGUTILITY_STRLEN("static mgutility::string_view " \
138 "mgutility::detail::enum_type::raw_name(mgutility::detail:" \
139 ":enum_sequence<Enum, e ...>) [with Enum = ")
140#elif defined(_MSC_VER)
141#if MGUTILITY_CPLUSPLUS > 201402L
142#define PREFIX \
143 MGUTILITY_STRLEN( \
144 "class std::basic_string_view<char,struct std::char_traits<char> > " \
145 "__cdecl mgutility::detail::enum_type::raw_name")
146#else
147#define PREFIX \
148 MGUTILITY_STRLEN("class mgutility::basic_string_view<char> __cdecl " \
149 "mgutility::detail::enum_type::raw_name")
150#endif
151#else
152#define PREFIX 0
153#endif
154
155 return mgutility::string_view(__PRETTY_FUNCTION__ + PREFIX,
156 MGUTILITY_STRLEN(__PRETTY_FUNCTION__) -
157 PREFIX + 1);
158 }
159
166 template <typename Enum, int Min, int Max>
167 MGUTILITY_CNSTXPR static auto parse() noexcept
169 using U = detail::underlying_type_t<Enum>;
170 using result_type = enum_name_parse_result<U, Min, Max>;
171
172 MGUTILITY_CNSTXPR auto str =
173 raw_name<Enum>(detail::make_enum_sequence<Enum, Min, Max>{});
174
175#if defined(__clang__) || defined(__GNUC__)
176#if defined(__clang__)
177 auto end = str.rfind(']');
178#elif defined(__GNUC__) && !defined(__clang__)
179 auto end = str.rfind(';');
180#endif
181
182 auto enum_names = str.substr(0, end);
183
184#elif defined(_MSC_VER)
185 // MSVC: different format
186 auto pos = str.find(',');
187 if (pos == mgutility::string_view::npos)
188 return {};
189
190 ++pos;
191
192 auto end = str.rfind('>');
193 auto enum_names = str.substr(pos, end - pos);
194
195#else
196 return {};
197#endif
198
199 if (enum_names.empty()) {
200 return {};
201 }
202
203 result_type result{};
204
205 std::size_t idx = 0;
206
207 while (!enum_names.empty() && idx < result.ranges.size()) {
208 auto pos = enum_names.find(',');
209 if (pos != mgutility::string_view::npos) {
210 auto token = enum_names.substr(0, pos);
211
212 // remove whitespace
213 while (!token.empty() && token.front() == ' ') {
214 token = token.substr(1);
215 }
216
217 while (!token.empty() && token.back() == ' ') {
218 token = token.substr(0, token.size() - 1);
219 }
220
221 std::size_t begin = token.find('(');
222 std::size_t end = token.rfind(')');
223 if (begin != mgutility::string_view::npos ||
224 end != mgutility::string_view::npos) {
225 result.ranges[idx++] = {0, 0};
226 enum_names = enum_names.substr(pos + 1);
227 continue;
228 }
229
230 std::size_t start = 0;
231 if (token.find(':') != mgutility::string_view::npos) {
232 start = token.find(':') + 2;
233 token = token.substr(start);
234 }
235
236 // Write into the fixed-size global buffer
237 auto offset = result.strings.size();
238 result.strings.append(token);
239 result.ranges[idx++] = {offset, token.size()};
240
241 enum_names = enum_names.substr(pos + 1);
242 continue;
243 }
244 enum_names = {};
245 }
246
247 return result;
248 }
249
250public:
258 template <typename Enum, int Min, int Max>
259 MGUTILITY_CNSTXPR static auto name() noexcept
260 -> enum_name_parse_result<detail::underlying_type_t<Enum>, Min, Max> {
261 return parse<Enum, Min, Max>();
262 }
263};
264
271template <typename Enum, int Min, int Max> struct enum_array_cache;
272
279template <typename Enum, int Min, int Max> struct enum_array_cache {
280 using underlying = detail::underlying_type_t<Enum>;
281 using parse_result_t = enum_name_parse_result<underlying, Min, Max>;
282
283#if MGUTILITY_CPLUSPLUS > 201402L
284
285 static constexpr auto parse_result =
286 enum_type::template name<Enum, Min, Max>();
287
288#else
289 // C++11: lazy runtime array
290 static parse_result_t &value() {
291 static parse_result_t arr = enum_type::template name<Enum, Min, Max>();
292
293 return arr;
294 }
295#endif
296
297 static MGUTILITY_CNSTXPR auto
298 apply_custom(const parse_result_t &result) noexcept
299 -> enum_name_array<Enum, Min, Max> {
300 enum_name_array<Enum, Min, Max> arr{};
301
302 for (std::size_t idx = 0; idx < result.ranges.size(); ++idx) {
303 arr[idx] = result.strings.view().substr(result.ranges[idx].first,
304 result.ranges[idx].second);
305 }
306
307#if MGUTILITY_CPLUSPLUS >= 201402L
308 constexpr auto map = mgutility::custom_enum<Enum>::map;
309 for (const auto &pair : map) {
310#else
311 for (const auto &pair : mgutility::custom_enum<Enum>::map) {
312#endif
313 if (pair.first >= static_cast<Enum>(Min) &&
314 pair.first < static_cast<Enum>(Max)) {
315 arr[static_cast<std::size_t>(pair.first) - Min] = pair.second;
316 }
317 }
318
319 return arr;
320 }
321};
322
332template <typename Enum, int Min = mgutility::enum_range<Enum>::min,
334MGUTILITY_CNSTXPR auto get_enum_array() noexcept
335 -> enum_name_array<Enum, Min, Max> {
336#if MGUTILITY_CPLUSPLUS > 201402L
337 constexpr auto &cache = enum_array_cache<Enum, Min, Max>::parse_result;
338 return enum_array_cache<Enum, Min, Max>::apply_custom(cache);
339#else
340 return enum_array_cache<Enum, Min, Max>::apply_custom(
341 enum_array_cache<Enum, Min, Max>::value());
342#endif
343}
344
354template <typename Enum, int Min, int Max>
355MGUTILITY_CNSTXPR inline auto to_enum_impl(mgutility::string_view str) noexcept
357 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
358
359 const auto index{detail::find(arr, str)};
360 return index == 0 ? mgutility::nullopt
361 : mgutility::optional<Enum>{static_cast<Enum>(index + Min)};
362}
363
373template <typename Enum, int Min, int Max>
374MGUTILITY_CNSTXPR auto to_enum_bitmask_impl(mgutility::string_view str) noexcept
376
377 // Check if the string contains a '|' character
378 if (str.find('|') == mgutility::string_view::npos) {
379 return to_enum_impl<Enum, Min, Max>(str);
380 }
381
383 std::size_t index = 0;
384
385 for (std::size_t i = 0; i < str.size(); ++i) {
386 if (str[i] == '|') {
387 auto name = str.substr(index, i - index);
388 auto maybe_enum = to_enum_impl<Enum, Min, Max>(name);
389
390 if (!name.empty() && maybe_enum) {
391 result.emplace(result ? static_cast<Enum>(*result | *maybe_enum)
392 : *maybe_enum);
393 }
394
395 index = i + 1;
396 }
397 }
398
399 auto maybe_enum = to_enum_impl<Enum, Min, Max>(str.substr(index));
400 if (result && maybe_enum) {
401 result.emplace(static_cast<Enum>(*result | *maybe_enum));
402 } else {
403 result.reset();
404 }
405
406 return result;
407}
408
418template <typename Enum, int Min, int Max,
419 detail::enable_if_t<!detail::has_bit_or<Enum>::value, bool> = true>
420MGUTILITY_CNSTXPR auto enum_name_impl(Enum enumValue) noexcept
422 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
423 const auto index{(Min < 0 ? -Min : Min) + static_cast<int>(enumValue)};
424 if (index < Min || index > static_cast<int>(arr.size()) - 1) {
425 return mgutility::string_view{};
426 }
427
428 return arr[static_cast<size_t>(index)];
429}
430
441// NOLINTNEXTLINE [modernize-use-constraints]
442template <typename Enum, int Min, int Max,
443 detail::enable_if_t<detail::has_bit_or<Enum>::value, bool> = true>
444MGUTILITY_CNSTXPR_CLANG_WA auto enum_name_impl(Enum enumValue) noexcept
446
447 // Get the array of enum names
448 MGUTILITY_CNSTXPR_CLANG_WA auto arr = get_enum_array<Enum, Min, Max>();
449
450 // Calculate the index in the array
451 const auto index = (Min < 0 ? -Min : Min) + static_cast<int>(enumValue);
452
454
455 if (index >= 0 && index < static_cast<int>(arr.size())) {
456 bitmasked_name.append(arr[static_cast<size_t>(index)]);
457 }
458
459 if (!bitmasked_name.empty()) {
460 return bitmasked_name;
461 }
462
463 for (auto i = 0; i < Max - Min; ++i) {
464 if (i >= 0 && i < static_cast<int>(arr.size()) && arr[i].size() > 0 &&
465 (enumValue & static_cast<Enum>(i)) == static_cast<Enum>(i)) {
466 bitmasked_name.append(arr[i]).append("|");
467 }
468 }
469
470 // Remove the trailing '|' if present
471 if (!bitmasked_name.empty()) {
472 bitmasked_name.pop_back();
473 }
474
475 return bitmasked_name;
476}
477} // namespace detail
478} // namespace mgutility
479
480#endif // MGUTILITY_REFLECTION_DETAIL_ENUM_NAME_IMPL_HPP
A basic string view class template.
Definition mgutility_enum_name.hpp:677
Definition mgutility_enum_name.hpp:980
A class template that provides optional (nullable) objects.
Definition mgutility_enum_name.hpp:1385
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:1643
Provides the custom names map for an enumeration type.
Definition meta.hpp:240
Parse result for enum names.
Definition enum_name_impl.hpp:103
Represents a sequence of enumeration values.
Definition meta.hpp:169
Provides functionality to extract and parse enum names at compile-time.
Definition enum_name_impl.hpp:117
static MGUTILITY_CNSTXPR auto name() noexcept -> enum_name_parse_result< detail::underlying_type_t< Enum >, Min, Max >
Gets the name of the enum value, checking custom names first.
Definition enum_name_impl.hpp:259
Provides the range for an enumeration type.
Definition meta.hpp:217