• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2021 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  #pragma once
18  
19  #include <cstddef>
20  #include <limits>
21  #include <optional>
22  #include <string_view>
23  #include <type_traits>
24  #include <utility>
25  
26  #include <ftl/string.h>
27  
28  // Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the
29  // compiler-generated string literal for the signature of this function. The function is defined in
30  // the global namespace with a short name and inferred return type to reduce bloat in the read-only
31  // data segment.
32  template <typename E, E V>
ftl_enum()33  constexpr auto ftl_enum() {
34    static_assert(std::is_enum_v<E>);
35  
36    using R = std::optional<std::string_view>;
37    using namespace std::literals;
38  
39    // The "pretty" signature has the following format:
40    //
41    //   auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue]
42    //
43    std::string_view view = __PRETTY_FUNCTION__;
44    const auto template_begin = view.rfind('[');
45    const auto template_end = view.rfind(']');
46    if (template_begin == view.npos || template_end == view.npos) return R{};
47  
48    // Extract the template parameters without the enclosing brackets. Example (cont'd):
49    //
50    //   E = android::test::Enum, V = android::test::Enum::kValue
51    //
52    view = view.substr(template_begin + 1, template_end - template_begin - 1);
53    const auto value_begin = view.rfind("V = "sv);
54    if (value_begin == view.npos) return R{};
55  
56    // Example (cont'd):
57    //
58    //   V = android::test::Enum::kValue
59    //
60    view = view.substr(value_begin);
61    const auto name_begin = view.rfind("::"sv);
62    if (name_begin == view.npos) return R{};
63  
64    // Chop off the leading "::".
65    const auto name = view.substr(name_begin + 2);
66  
67    // A value that is not enumerated has the format "Enum)42".
68    return name.find(')') == view.npos ? R{name} : R{};
69  }
70  
71  namespace android::ftl {
72  
73  // Trait for determining whether a type is specifically a scoped enum or not. By definition, a
74  // scoped enum is one that is not implicitly convertible to its underlying type.
75  //
76  // TODO: Replace with std::is_scoped_enum in C++23.
77  //
78  template <typename T, bool = std::is_enum_v<T>>
79  struct is_scoped_enum : std::false_type {};
80  
81  template <typename T>
82  struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
83  };
84  
85  template <typename T>
86  inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
87  
88  // Shorthand for casting an enumerator to its integral value.
89  //
90  // TODO: Replace with std::to_underlying in C++23.
91  //
92  //   enum class E { A, B, C };
93  //   static_assert(ftl::to_underlying(E::B) == 1);
94  //
95  template <typename E>
96  constexpr auto to_underlying(E v) {
97    return static_cast<std::underlying_type_t<E>>(v);
98  }
99  
100  // Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
101  // ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
102  // where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
103  // enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
104  // range values results in undefined behavior if the underlying type is not fixed.
105  //
106  //   enum class E { A, B, C, F = 5, ftl_last = F };
107  //
108  //   static_assert(ftl::enum_begin_v<E> == E::A);
109  //   static_assert(ftl::enum_last_v<E> == E::F);
110  //   static_assert(ftl::enum_size_v<E> == 6);
111  //
112  //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
113  //
114  //   static_assert(ftl::enum_begin_v<F> == F{0});
115  //   static_assert(ftl::enum_last_v<F> == F{15});
116  //   static_assert(ftl::enum_size_v<F> == 16);
117  //
118  template <typename E, typename = void>
119  struct enum_begin {
120    static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
121    static constexpr E value{0};
122  };
123  
124  template <typename E>
125  struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
126    static constexpr E value = E::ftl_first;
127  };
128  
129  template <typename E>
130  inline constexpr E enum_begin_v = enum_begin<E>::value;
131  
132  template <typename E, typename = void>
133  struct enum_end {
134    using U = std::underlying_type_t<E>;
135    static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
136  
137    static constexpr E value{std::numeric_limits<U>::digits};
138  };
139  
140  template <typename E>
141  struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
142    static constexpr E value = E{to_underlying(E::ftl_last) + 1};
143  };
144  
145  template <typename E>
146  inline constexpr E enum_end_v = enum_end<E>::value;
147  
148  template <typename E>
149  inline constexpr E enum_last_v = E{to_underlying(enum_end_v<E>) - 1};
150  
151  template <typename E>
152  struct enum_size {
153    static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
154    static constexpr auto kEnd = to_underlying(enum_end_v<E>);
155    static_assert(kBegin < kEnd, "Invalid range");
156  
157    static constexpr std::size_t value = kEnd - kBegin;
158    static_assert(value <= 64, "Excessive range size");
159  };
160  
161  template <typename E>
162  inline constexpr std::size_t enum_size_v = enum_size<E>::value;
163  
164  namespace details {
165  
166  template <auto V>
167  struct Identity {
168    static constexpr auto value = V;
169  };
170  
171  template <typename E>
172  using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
173  
174  template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
175  struct EnumRange;
176  
177  template <typename E, template <E> class F, typename T, T... Vs>
178  struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
179    static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
180    static constexpr auto kSize = enum_size_v<E>;
181  
182    using R = decltype(F<E{}>::value);
183    const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
184  
185    constexpr const auto* begin() const { return values; }
186    constexpr const auto* end() const { return values + kSize; }
187  };
188  
189  template <auto V>
190  struct EnumName {
191    static constexpr auto value = ftl_enum<decltype(V), V>();
192  };
193  
194  template <auto I>
195  struct FlagName {
196    using E = decltype(I);
197    using U = std::underlying_type_t<E>;
198  
199    static constexpr E V{U{1} << to_underlying(I)};
200    static constexpr auto value = ftl_enum<E, V>();
201  };
202  
203  }  // namespace details
204  
205  // Returns an iterable over the range of an enum.
206  //
207  //   enum class E { A, B, C, F = 5, ftl_last = F };
208  //
209  //   std::string string;
210  //   for (E v : ftl::enum_range<E>()) {
211  //     string += ftl::enum_name(v).value_or("?");
212  //   }
213  //
214  //   assert(string == "ABC??F");
215  //
216  template <typename E>
217  constexpr auto enum_range() {
218    return details::EnumRange<E>{};
219  }
220  
221  // Returns a stringified enumerator at compile time.
222  //
223  //   enum class E { A, B, C };
224  //   static_assert(ftl::enum_name<E::B>() == "B");
225  //
226  template <auto V>
227  constexpr std::string_view enum_name() {
228    constexpr auto kName = ftl_enum<decltype(V), V>();
229    static_assert(kName, "Unknown enumerator");
230    return *kName;
231  }
232  
233  // Returns a stringified enumerator, possibly at compile time.
234  //
235  //   enum class E { A, B, C, F = 5, ftl_last = F };
236  //
237  //   static_assert(ftl::enum_name(E::C).value_or("?") == "C");
238  //   static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
239  //
240  template <typename E>
241  constexpr std::optional<std::string_view> enum_name(E v) {
242    const auto value = to_underlying(v);
243  
244    constexpr auto kBegin = to_underlying(enum_begin_v<E>);
245    constexpr auto kLast = to_underlying(enum_last_v<E>);
246    if (value < kBegin || value > kLast) return {};
247  
248    constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
249    return kRange.values[value - kBegin];
250  }
251  
252  // Returns a stringified flag enumerator, possibly at compile time.
253  //
254  //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
255  //
256  //   static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
257  //   static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
258  //
259  template <typename E>
260  constexpr std::optional<std::string_view> flag_name(E v) {
261    const auto value = to_underlying(v);
262  
263    // TODO: Replace with std::popcount and std::countr_zero in C++20.
264    if (__builtin_popcountll(value) != 1) return {};
265  
266    constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
267    return kRange.values[__builtin_ctzll(value)];
268  }
269  
270  // Returns a stringified enumerator, or its integral value if not named.
271  //
272  //   enum class E { A, B, C, F = 5, ftl_last = F };
273  //
274  //   assert(ftl::enum_string(E::C) == "C");
275  //   assert(ftl::enum_string(E{3}) == "3");
276  //
277  template <typename E>
278  inline std::string enum_string(E v) {
279    if (const auto name = enum_name(v)) {
280      return std::string(*name);
281    }
282    return to_string(to_underlying(v));
283  }
284  
285  // Returns a stringified flag enumerator, or its integral value if not named.
286  //
287  //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
288  //
289  //   assert(ftl::flag_string(F::Z) == "Z");
290  //   assert(ftl::flag_string(F{7}) == "0b111");
291  //
292  template <typename E>
293  inline std::string flag_string(E v) {
294    if (const auto name = flag_name(v)) {
295      return std::string(*name);
296    }
297    constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
298    return to_string(to_underlying(v), radix);
299  }
300  
301  }  // namespace android::ftl
302