VLink  2.0.0
A high-performance communication middleware
name_detector.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file name_detector.h
26  * @brief Header-only compile-time introspection of type names and enumerator labels.
27  *
28  * @details
29  * @c vlink::NameDetector exposes two primitives that resolve human-readable identifiers
30  * at compile time without RTTI or any third-party dependency. The implementation walks
31  * the textual content of @c __PRETTY_FUNCTION__ on Clang/GCC and @c __FUNCSIG__ on MSVC,
32  * then trims compiler-specific noise so the result is identical across toolchains.
33  *
34  * Validation rules applied by the internal @c pretty_name() pass:
35  *
36  * | Rule | Result |
37  * | ------------------------------------- | -------------------------------------------- |
38  * | Begins with a character literal quote | Returns an empty view (not a valid name) |
39  * | Begins with a digit | Returns an empty view (numeric literal) |
40  * | Contains a trailing parenthesised arg | Strips the argument list before the name |
41  * | Contains a trailing template list | Strips the @c "<...>" suffix from the name |
42  * | Final character is not alnum/under | Trims back to the last identifier character |
43  * | First character is not letter/under | Returns an empty view (rejects the result) |
44  *
45  * @par Example
46  * @code
47  * static_assert(vlink::NameDetector::is_support<MyPlugin>());
48  * constexpr std::string_view kName = vlink::NameDetector::get<MyPlugin>();
49  *
50  * enum class Color { Red, Green, Blue };
51  * std::string_view label = vlink::NameDetector::get_enum(Color::Green);
52  * @endcode
53  */
54 
55 #pragma once
56 
57 #include <array>
58 #include <cassert>
59 #include <cstddef>
60 #include <cstdint>
61 #include <limits>
62 #include <string_view>
63 #include <type_traits>
64 #include <utility>
65 
66 #if defined(__clang__)
67 #pragma clang diagnostic push
68 #pragma clang diagnostic ignored "-Wunknown-warning-option"
69 #pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
70 #elif defined(__GNUC__)
71 #pragma GCC diagnostic push
72 #pragma GCC diagnostic ignored "-Wstringop-overflow"
73 #elif defined(_MSC_VER)
74 #pragma warning(push)
75 #pragma warning(disable : 26495)
76 #pragma warning(disable : 28020)
77 
78 #pragma warning(disable : 26451)
79 #pragma warning(disable : 4514)
80 #endif
81 
82 #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 7 || \
83  defined(_MSC_VER) && _MSC_VER >= 1910
84 #undef VLINK_NAME_DETECTOR_TYPE_SUPPORTED
85 #define VLINK_NAME_DETECTOR_TYPE_SUPPORTED 1
86 #endif
87 
88 #if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || \
89  defined(_MSC_VER) && _MSC_VER >= 1910
90 #undef VLINK_NAME_DETECTOR_ENUM_SUPPORTED
91 #define VLINK_NAME_DETECTOR_ENUM_SUPPORTED 1
92 #endif
93 
94 #if !defined(VLINK_NAME_DETECTOR_ENUM_RANGE_MIN)
95 #define VLINK_NAME_DETECTOR_ENUM_RANGE_MIN -128
96 #endif
97 
98 #if !defined(VLINK_NAME_DETECTOR_ENUM_RANGE_MAX)
99 #define VLINK_NAME_DETECTOR_ENUM_RANGE_MAX 128
100 #endif
101 
102 namespace vlink {
103 
104 /**
105  * @namespace vlink::NameDetector
106  * @brief Compile-time type-name and enum-name reflection primitives.
107  */
108 namespace NameDetector { // NOLINT(readability-identifier-naming)
109 
110 /**
111  * @brief Reports whether the current toolchain can extract a textual name for @p TypeT.
112  *
113  * @tparam TypeT Type whose support status is queried.
114  * @return @c true when @c get<TypeT>() returns a meaningful non-empty view on this compiler.
115  */
116 template <typename TypeT>
117 [[nodiscard]] constexpr bool is_support() noexcept;
118 
119 /**
120  * @brief Returns the trimmed compile-time identifier for @p TypeT.
121  *
122  * @details
123  * MSVC inserts a @c "struct " or @c "class " prefix into @c __FUNCSIG__; both are stripped
124  * before the value is returned so the same textual identifier is produced on every supported
125  * toolchain. The returned view always points at static read-only storage.
126  *
127  * @tparam TypeT Type whose identifier is requested.
128  * @return Read-only @c std::string_view with the resolved identifier.
129  */
130 template <typename TypeT>
131 [[nodiscard]] constexpr std::string_view get() noexcept;
132 
133 /**
134  * @brief Resolves the source identifier of an enumerator literal at compile time.
135  *
136  * @tparam EnumT Enumeration type, deduced from @p value.
137  * @param value Enumerator value whose label is requested.
138  * @return Identifier text, or an empty view when @p value is outside the configured
139  * scanning window or is not a named enumerator.
140  */
141 template <typename EnumT>
142 [[nodiscard]] constexpr std::string_view get_enum(EnumT value) noexcept;
143 
144 /**
145  * @namespace vlink::NameDetector::customize
146  * @brief Customisation points overriding the default enumerator scanning window.
147  *
148  * @details
149  * Specialise @c EnumRange<EnumT> on a per-type basis to widen or narrow the
150  * compile-time search bounds used by @c get_enum().
151  */
152 namespace customize { // NOLINT(readability-identifier-naming)
153 
154 /**
155  * @struct EnumRange
156  * @brief Inclusive integer range scanned for @p EnumT enumerator labels.
157  *
158  * @tparam EnumT Enumeration type for which the range is being customised.
159  */
160 template <typename EnumT>
161 struct EnumRange {
162  static_assert(std::is_enum_v<EnumT>, "vlink::NameDetector::customize::EnumRange requires enum type.");
163  inline static constexpr int kMin = VLINK_NAME_DETECTOR_ENUM_RANGE_MIN; ///< Inclusive lower scan bound.
164  inline static constexpr int kMax = VLINK_NAME_DETECTOR_ENUM_RANGE_MAX; ///< Inclusive upper scan bound.
165  static_assert(kMax > kMin, "vlink::NameDetector::customize::EnumRange requires kMax > kMin.");
166 };
167 
168 static_assert(VLINK_NAME_DETECTOR_ENUM_RANGE_MIN <= 0,
169  "VLINK_NAME_DETECTOR_ENUM_RANGE_MIN must be less or equals than 0.");
170 // NOLINTNEXTLINE(readability-redundant-parentheses)
171 static_assert(VLINK_NAME_DETECTOR_ENUM_RANGE_MIN > (std::numeric_limits<int16_t>::min)(),
172  "VLINK_NAME_DETECTOR_ENUM_RANGE_MIN must be greater than INT16_MIN.");
173 
174 static_assert(VLINK_NAME_DETECTOR_ENUM_RANGE_MAX > 0, "VLINK_NAME_DETECTOR_ENUM_RANGE_MAX must be greater than 0.");
175 // NOLINTNEXTLINE(readability-redundant-parentheses)
176 static_assert(VLINK_NAME_DETECTOR_ENUM_RANGE_MAX < (std::numeric_limits<int16_t>::max)(),
177  "VLINK_NAME_DETECTOR_ENUM_RANGE_MAX must be less than INT16_MAX.");
178 
180  "VLINK_NAME_DETECTOR_ENUM_RANGE_MAX must be greater than VLINK_NAME_DETECTOR_ENUM_RANGE_MIN.");
181 
182 } // namespace customize
183 
184 } // namespace NameDetector
185 
186 ////////////////////////////////////////////////////////////////
187 /// Details
188 ////////////////////////////////////////////////////////////////
189 
190 namespace NameDetector {
191 namespace detail { // NOLINT(readability-identifier-naming)
192 
193 template <typename TypeT>
195 #if defined(VLINK_NAME_DETECTOR_TYPE_SUPPORTED) && VLINK_NAME_DETECTOR_TYPE_SUPPORTED
196  : std::true_type {
197 };
198 #else
199  : std::false_type {
200 };
201 #endif
202 
203 template <typename TypeT>
205 #if defined(VLINK_NAME_DETECTOR_ENUM_SUPPORTED) && VLINK_NAME_DETECTOR_ENUM_SUPPORTED
206  : std::true_type {
207 };
208 #else
209  : std::false_type {
210 };
211 #endif
212 
213 template <typename TypeT>
214 inline constexpr bool kIsEnum = std::is_enum_v<TypeT> && std::is_same_v<TypeT, std::decay_t<TypeT>>;
215 
216 template <typename TypeT>
217 using RemoveCvrefT = std::remove_cv_t<std::remove_reference_t<TypeT>>;
218 
219 template <uint16_t SizeT>
220 class [[nodiscard]] CString {
221  public:
222  constexpr explicit CString(std::string_view str) noexcept
223  : CString{str, std::make_integer_sequence<uint16_t, SizeT>{}} {
224  assert(str.size() == SizeT);
225  }
226  constexpr CString() noexcept : chars_{} {}
227  constexpr CString(const CString&) = default;
228  constexpr CString(CString&&) noexcept = default;
229  ~CString() = default;
230  CString& operator=(const CString&) = default;
231  CString& operator=(CString&&) noexcept = default;
232 
233  [[nodiscard]] constexpr const char* data() const noexcept { return chars_; }
234  [[nodiscard]] constexpr uint16_t size() const noexcept { return SizeT; }
235  [[nodiscard]] constexpr bool empty() const noexcept { return SizeT == 0; }
236 
237  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
238  [[nodiscard]] constexpr operator std::string_view() const noexcept { return std::string_view{data(), size()}; }
239 
240  private:
241  template <uint16_t... IndexT>
242  constexpr CString(std::string_view str, std::integer_sequence<uint16_t, IndexT...>) noexcept
243  : chars_{str[IndexT]..., '\0'} {}
244 
245  // NOLINTNEXTLINE(runtime/arrays)
246  char chars_[static_cast<size_t>(SizeT) + 1];
247 };
248 
249 constexpr std::string_view pretty_name(std::string_view name, bool remove_suffix = true) noexcept {
250  if (!name.empty() && (name[0] == '"' || name[0] == '\'')) {
251  return {};
252  } else if (name.size() >= 2 && name[0] == 'R' && (name[1] == '"' || name[1] == '\'')) {
253  return {};
254  } else if (name.size() >= 2 && name[0] == 'L' && (name[1] == '"' || name[1] == '\'')) {
255  return {};
256  } else if (name.size() >= 2 && name[0] == 'U' && (name[1] == '"' || name[1] == '\'')) {
257  return {};
258  } else if (name.size() >= 2 && name[0] == 'u' && (name[1] == '"' || name[1] == '\'')) {
259  return {};
260  } else if (name.size() >= 3 && name[0] == 'u' && name[1] == '8' && (name[2] == '"' || name[2] == '\'')) {
261  return {};
262  } else if (!name.empty() && (name[0] >= '0' && name[0] <= '9')) {
263  return {};
264  }
265 
266  for (size_t i = name.size(), h = 0, s = 0; i > 0; --i) {
267  if (name[i - 1] == ')') {
268  ++h;
269  ++s;
270  continue;
271  } else if (name[i - 1] == '(') {
272  --h;
273  ++s;
274  continue;
275  }
276 
277  if (h == 0) {
278  name.remove_suffix(s);
279  break;
280  } else {
281  ++s;
282  continue;
283  }
284  }
285 
286  size_t s = 0;
287  for (size_t i = name.size(), h = 0; i > 0; --i) {
288  if (name[i - 1] == '>') {
289  ++h;
290  ++s;
291  continue;
292  } else if (name[i - 1] == '<') {
293  --h;
294  ++s;
295  continue;
296  }
297 
298  if (h == 0) {
299  break;
300  } else {
301  ++s;
302  continue;
303  }
304  }
305 
306  for (size_t i = name.size() - s; i > 0; --i) {
307  if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || (name[i - 1] >= 'a' && name[i - 1] <= 'z') ||
308  (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || (name[i - 1] == '_'))) {
309  name.remove_prefix(i);
310  break;
311  }
312  }
313 
314  if (remove_suffix) {
315  name.remove_suffix(s);
316  }
317 
318  if (!name.empty() && ((name[0] >= 'a' && name[0] <= 'z') || (name[0] >= 'A' && name[0] <= 'Z') || (name[0] == '_'))) {
319  return name;
320  }
321 
322  return {};
323 }
324 
325 #if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L
326 #define VLINK_NAME_DETECTOR_ARRAY_CONSTEXPR 1
327 #else
328 template <typename TypeT, size_t SizeT, size_t... IndexT>
329 constexpr std::array<std::remove_cv_t<TypeT>, SizeT> to_array(TypeT (&a)[SizeT],
330  std::index_sequence<IndexT...>) noexcept {
331  return {{a[IndexT]...}};
332 }
333 #endif
334 
335 template <typename LeftT, typename RightT>
336 constexpr bool cmp_less(LeftT lhs, RightT rhs) noexcept {
337  static_assert(std::is_integral_v<LeftT> && std::is_integral_v<RightT>,
338  "vlink::NameDetector::detail::cmp_less requires integral type.");
339 
340  if constexpr (std::is_signed_v<LeftT> == std::is_signed_v<RightT>) {
341  return lhs < rhs;
342  } else if constexpr (std::is_same_v<LeftT, bool>) {
343  return static_cast<RightT>(lhs) < rhs;
344  } else if constexpr (std::is_same_v<RightT, bool>) {
345  return lhs < static_cast<LeftT>(rhs);
346  } else if constexpr (std::is_signed_v<RightT>) {
347  return rhs > 0 && lhs < static_cast<std::make_unsigned_t<RightT>>(rhs);
348  } else {
349  return lhs < 0 || static_cast<std::make_unsigned_t<LeftT>>(lhs) < rhs;
350  }
351 }
352 
353 template <typename EnumT, EnumT ValueV>
354 constexpr auto raw_enum_name() noexcept {
355  static_assert(kIsEnum<EnumT>, "vlink::NameDetector::detail::raw_enum_name requires enum type.");
356 
357  if constexpr (EnumNameSupported<EnumT>::value) {
358 #if defined(__clang__) || defined(__GNUC__)
359  constexpr auto kName = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2});
360 #elif defined(_MSC_VER)
361  constexpr auto kName = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17});
362 #else
363  constexpr auto kName = std::string_view{};
364 #endif
365  return kName;
366  } else {
367  return std::string_view{};
368  }
369 }
370 
371 template <typename EnumT, EnumT ValueV>
372 constexpr auto enum_name() noexcept {
373  constexpr auto kName = raw_enum_name<EnumT, ValueV>();
374  return CString<kName.size()>{kName};
375 }
376 
377 template <typename EnumT, EnumT ValueV>
378 // NOLINTNEXTLINE(google-readability-casting,modernize-avoid-c-style-cast)
379 inline constexpr auto kEnumName = enum_name<EnumT, ValueV>();
380 
381 template <typename EnumT, auto ValueV>
382 constexpr bool is_valid() noexcept {
383 #if defined(__clang__) && __clang_major__ >= 16
384  constexpr auto kV = __builtin_bit_cast(EnumT, ValueV);
385 #else
386  constexpr auto kV = static_cast<EnumT>(ValueV);
387 #endif
388  return raw_enum_name<EnumT, kV>().size() != 0;
389 }
390 
391 template <typename EnumT, int OffsetT, typename UnderlyingT = std::underlying_type_t<EnumT>>
392 constexpr UnderlyingT ualue(size_t i) noexcept {
393  if constexpr (std::is_same_v<UnderlyingT, bool>) {
394  static_assert(OffsetT == 0, "vlink::NameDetector::detail::ualue requires zero offset for bool.");
395  return static_cast<UnderlyingT>(i);
396  } else {
397  return static_cast<UnderlyingT>(static_cast<int>(i) + OffsetT);
398  }
399 }
400 
401 template <typename EnumT, int OffsetT, typename UnderlyingT = std::underlying_type_t<EnumT>>
402 constexpr EnumT enum_value_at(size_t i) noexcept {
403  return static_cast<EnumT>(ualue<EnumT, OffsetT>(i));
404 }
405 
406 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
407 constexpr int reflected_min() noexcept {
408  constexpr auto kLhs = customize::EnumRange<EnumT>::kMin;
409  // NOLINTNEXTLINE(readability-redundant-parentheses)
410  constexpr auto kRhs = (std::numeric_limits<UnderlyingT>::min)();
411 
412  // NOLINTNEXTLINE(readability-suspicious-call-argument)
413  if constexpr (cmp_less(kRhs, kLhs)) {
414  return kLhs;
415  } else {
416  return kRhs;
417  }
418 }
419 
420 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
421 constexpr int reflected_max() noexcept {
422  constexpr auto kLhs = customize::EnumRange<EnumT>::kMax;
423  // NOLINTNEXTLINE(readability-redundant-parentheses)
424  constexpr auto kRhs = (std::numeric_limits<UnderlyingT>::max)();
425 
426  if constexpr (cmp_less(kLhs, kRhs)) {
427  return kLhs;
428  } else {
429  return kRhs;
430  }
431 }
432 
433 // clang-format off
434 #define VLINK_NAME_DETECTOR_FOR_EACH_256(X) \
435  X(0) \
436  X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) \
437  X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31) X(32) X(33) X(34) X(35) X(36) X(37) X(38) X(39) X(40) \
438  X(41) X(42) X(43) X(44) X(45) X(46) X(47) X(48) X(49) X(50) X(51) X(52) X(53) X(54) X(55) X(56) X(57) X(58) X(59) \
439  X(60) X(61) X(62) X(63) X(64) X(65) X(66) X(67) X(68) X(69) X(70) X(71) X(72) X(73) X(74) X(75) X(76) X(77) X(78) \
440  X(79) X(80) X(81) X(82) X(83) X(84) X(85) X(86) X(87) X(88) X(89) X(90) X(91) X(92) X(93) X(94) X(95) X(96) X(97) \
441  X(98) X(99) X(100) X(101) X(102) X(103) X(104) X(105) X(106) X(107) X(108) X(109) X(110) X(111) X(112) X(113) \
442  X(114) X(115) X(116) X(117) X(118) X(119) X(120) X(121) X(122) X(123) X(124) X(125) X(126) X(127) X(128) X(129) \
443  X(130) X(131) X(132) X(133) X(134) X(135) X(136) X(137) X(138) X(139) X(140) X(141) X(142) X(143) X(144) X(145) \
444  X(146) X(147) X(148) X(149) X(150) X(151) X(152) X(153) X(154) X(155) X(156) X(157) X(158) X(159) X(160) X(161) \
445  X(162) X(163) X(164) X(165) X(166) X(167) X(168) X(169) X(170) X(171) X(172) X(173) X(174) X(175) X(176) X(177) \
446  X(178) X(179) X(180) X(181) X(182) X(183) X(184) X(185) X(186) X(187) X(188) X(189) X(190) X(191) X(192) X(193) \
447  X(194) X(195) X(196) X(197) X(198) X(199) X(200) X(201) X(202) X(203) X(204) X(205) X(206) X(207) X(208) X(209) \
448  X(210) X(211) X(212) X(213) X(214) X(215) X(216) X(217) X(218) X(219) X(220) X(221) X(222) X(223) X(224) X(225) \
449  X(226) X(227) X(228) X(229) X(230) X(231) X(232) X(233) X(234) X(235) X(236) X(237) X(238) X(239) X(240) X(241) \
450  X(242) X(243) X(244) X(245) X(246) X(247) X(248) X(249) X(250) X(251) X(252) X(253) X(254) X(255)
451 // clang-format on
452 
453 template <typename EnumT, size_t SizeT, int MinT, size_t IndexT>
454 // NOLINTNEXTLINE(google-readability-function-size,readability-function-size)
455 constexpr void valid_count_step(bool* valid, size_t& count) noexcept {
456 #define VLINK_NAME_DETECTOR_ENUM_V(O) \
457  if constexpr ((IndexT + (O)) < SizeT) { \
458  if constexpr (is_valid<EnumT, ualue<EnumT, MinT>(IndexT + (O))>()) { \
459  valid[IndexT + (O)] = true; \
460  ++count; \
461  } \
462  }
464 #undef VLINK_NAME_DETECTOR_ENUM_V
465 
466  if constexpr ((IndexT + 256) < SizeT) {
467  valid_count_step<EnumT, SizeT, MinT, IndexT + 256>(valid, count);
468  }
469 }
470 
471 template <size_t SizeT>
473  size_t count = 0;
474  bool valid[SizeT] = {};
475 };
476 
477 template <typename EnumT, size_t SizeT, int MinT>
478 constexpr auto valid_count() noexcept {
480  valid_count_step<EnumT, SizeT, MinT, 0>(result.valid, result.count);
481  return result;
482 }
483 
484 template <typename EnumT, size_t SizeT, int MinT>
485 constexpr auto values_in_range() noexcept {
486  constexpr auto kVc = valid_count<EnumT, SizeT, MinT>();
487 
488  if constexpr (kVc.count > 0) {
489 #if defined(VLINK_NAME_DETECTOR_ARRAY_CONSTEXPR)
490  // NOLINTNEXTLINE(bugprone-invalid-enum-default-initialization)
491  std::array<EnumT, kVc.count> result = {};
492 #else
493  EnumT result[kVc.count] = {};
494 #endif
495  for (size_t i = 0, v = 0; v < kVc.count; ++i) {
496  if (kVc.valid[i]) {
497  result[v++] = enum_value_at<EnumT, MinT>(i);
498  }
499  }
500 #if defined(VLINK_NAME_DETECTOR_ARRAY_CONSTEXPR)
501  return result;
502 #else
503  return to_array(result, std::make_index_sequence<kVc.count>{});
504 #endif
505  } else {
506  return std::array<EnumT, 0>{};
507  }
508 }
509 
510 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
511 constexpr auto values() noexcept {
512  constexpr auto kMin = reflected_min<EnumT>();
513  constexpr auto kMax = reflected_max<EnumT>();
514  constexpr auto kRangeSize = kMax - kMin + 1;
515  static_assert(kRangeSize > 0, "vlink::NameDetector enum range requires positive size.");
516  // NOLINTNEXTLINE(readability-redundant-parentheses)
517  static_assert(kRangeSize < (std::numeric_limits<uint16_t>::max)(),
518  "vlink::NameDetector enum range exceeds supported size.");
519 
520  return values_in_range<EnumT, kRangeSize, kMin>();
521 }
522 
523 template <typename EnumT>
524 inline constexpr auto kValues = values<EnumT>();
525 
526 template <typename EnumT>
527 inline constexpr auto kCount = kValues<EnumT>.size();
528 
529 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
530 inline constexpr UnderlyingT kMinTalue =
531  (kCount<EnumT> > 0) ? static_cast<UnderlyingT>(kValues<EnumT>.front()) : UnderlyingT{0};
532 
533 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
534 inline constexpr UnderlyingT kMaxValue =
535  (kCount<EnumT> > 0) ? static_cast<UnderlyingT>(kValues<EnumT>.back()) : UnderlyingT{0};
536 
537 template <typename EnumT, size_t... IndexT>
538 constexpr auto names_impl(std::index_sequence<IndexT...>) noexcept {
539  return std::array<std::string_view, sizeof...(IndexT)>{{kEnumName<EnumT, kValues<EnumT>[IndexT]>...}};
540 }
541 
542 template <typename EnumT>
543 inline constexpr auto kNames = names_impl<EnumT>(std::make_index_sequence<kCount<EnumT>>{});
544 
545 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
546 constexpr bool is_sparse() noexcept {
547  if constexpr (kCount<EnumT> == 0) {
548  return false;
549  } else if constexpr (std::is_same_v<UnderlyingT, bool>) {
550  return false;
551  } else {
552  constexpr auto kMaxV = kMaxValue<EnumT>;
553  constexpr auto kMinT = kMinTalue<EnumT>;
554  constexpr auto kRangeSize = kMaxV - kMinT + 1;
555  return static_cast<size_t>(kRangeSize) != kCount<EnumT>;
556  }
557 }
558 
559 template <typename EnumT>
560 inline constexpr bool kIsSparse = is_sparse<EnumT>();
561 
562 template <typename EnumT, typename UnderlyingT = std::underlying_type_t<EnumT>>
563 constexpr EnumT enum_value(size_t i) noexcept {
564  if constexpr (kIsSparse<EnumT>) {
565  return kValues<EnumT>[i];
566  } else {
567  constexpr auto kMinT = kMinTalue<EnumT>;
568  return enum_value_at<EnumT, static_cast<int>(kMinT)>(i);
569  }
570 }
571 
572 template <typename TypeT>
573 constexpr auto raw_type_name() noexcept {
574  if constexpr (TypeNameSupported<TypeT>::value) {
575 #if defined(__clang__)
576  constexpr std::string_view kName{__PRETTY_FUNCTION__ + 59, sizeof(__PRETTY_FUNCTION__) - 61};
577 #elif defined(__GNUC__)
578  constexpr std::string_view kName{__PRETTY_FUNCTION__ + 74, sizeof(__PRETTY_FUNCTION__) - 76};
579 #elif defined(_MSC_VER)
580  constexpr std::string_view kName{__FUNCSIG__ + 56,
581  sizeof(__FUNCSIG__) - 73 - (__FUNCSIG__[sizeof(__FUNCSIG__) - 18] == ' ' ? 1 : 0)};
582 #else
583  constexpr auto kName = std::string_view{};
584 #endif
585  return CString<static_cast<uint16_t>(kName.size())>{kName};
586  } else {
587  return CString<0>{};
588  }
589 }
590 
591 template <typename TypeT>
592 inline constexpr auto kTypeName = raw_type_name<TypeT>();
593 
594 } // namespace detail
595 } // namespace NameDetector
596 
597 template <typename TypeT>
598 inline constexpr bool NameDetector::is_support() noexcept {
600 }
601 
602 template <typename TypeT>
603 inline constexpr std::string_view NameDetector::get() noexcept {
604  using DecayT = detail::RemoveCvrefT<TypeT>;
605 
606  static_assert(detail::TypeNameSupported<DecayT>::value, "vlink::NameDetector::get requires a supported compiler.");
607 
608 #if defined(_WIN32) || defined(__CYGWIN__)
609  std::string_view view = detail::kTypeName<DecayT>;
610 
611  constexpr std::string_view kStructPrefix = "struct ";
612 
613  if (view.size() >= kStructPrefix.size() && view.substr(0, kStructPrefix.size()) == kStructPrefix) {
614  view.remove_prefix(kStructPrefix.size());
615  }
616 
617  constexpr std::string_view kClassPrefix = "class ";
618 
619  if (view.size() >= kClassPrefix.size() && view.substr(0, kClassPrefix.size()) == kClassPrefix) {
620  view.remove_prefix(kClassPrefix.size());
621  }
622 
623  return view;
624 #else
625  return detail::kTypeName<DecayT>;
626 #endif
627 }
628 
629 template <typename EnumT>
630 inline constexpr std::string_view NameDetector::get_enum(EnumT value) noexcept {
631  static_assert(std::is_enum_v<EnumT>, "vlink::NameDetector::get_enum requires enum type.");
632 
633  using DecayT = std::decay_t<EnumT>;
634  using UnderlyingT = std::underlying_type_t<DecayT>;
635 
637  "vlink::NameDetector::get_enum requires a supported compiler.");
638  static_assert(detail::kCount<DecayT> > 0,
639  "vlink::NameDetector::get_enum requires at least one enumerator within range.");
640 
641  if constexpr (detail::kIsSparse<DecayT>) {
642  for (size_t i = 0; i < detail::kCount<DecayT>; ++i) {
643  if (detail::enum_value<DecayT>(i) == value) {
644  return detail::kNames<DecayT>[i];
645  }
646  }
647  } else {
648  const auto v = static_cast<UnderlyingT>(value);
649 
650  if (v >= detail::kMinTalue<DecayT> && v <= detail::kMaxValue<DecayT>) {
651  return detail::kNames<DecayT>[static_cast<size_t>(v - detail::kMinTalue<DecayT>)];
652  }
653  }
654 
655  return {};
656 }
657 
658 } // namespace vlink
659 
660 #if defined(__clang__)
661 #pragma clang diagnostic pop
662 #elif defined(__GNUC__)
663 #pragma GCC diagnostic pop
664 #elif defined(_MSC_VER)
665 #pragma warning(pop)
666 #endif
#define VLINK_NAME_DETECTOR_FOR_EACH_256(X)
Definition: name_detector.h:434
#define VLINK_NAME_DETECTOR_ENUM_V(O)
#define VLINK_NAME_DETECTOR_ENUM_RANGE_MAX
Definition: name_detector.h:99
#define VLINK_NAME_DETECTOR_ENUM_RANGE_MIN
Definition: name_detector.h:95