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 and optionally the class (i.e. "E::V" or "V") as
29 // std::optional<std::string_view> by parsing the compiler-generated string literal for the
30 // signature of this function. The function is defined in the global namespace with a short name
31 // and inferred return type to reduce bloat in the read-only data segment.
32 template <bool S, typename E, E V>
ftl_enum_builder()33 constexpr auto ftl_enum_builder() {
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 pos = S ? view.rfind("::"sv) - 2 : view.npos;
62 
63   const auto name_begin = view.rfind("::"sv, pos);
64   if (name_begin == view.npos) return R{};
65 
66   // Chop off the leading "::".
67   const auto name = view.substr(name_begin + 2);
68 
69   // A value that is not enumerated has the format "Enum)42".
70   return name.find(')') == view.npos ? R{name} : R{};
71 }
72 
73 // Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view>
74 template <typename E, E V>
ftl_enum()75 constexpr auto ftl_enum() {
76   return ftl_enum_builder<false, E, V>();
77 }
78 
79 // Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional<std::string_view>
80 template <typename E, E V>
ftl_enum_full()81 constexpr auto ftl_enum_full() {
82   return ftl_enum_builder<true, E, V>();
83 }
84 
85 namespace android::ftl {
86 
87 // Trait for determining whether a type is specifically a scoped enum or not. By definition, a
88 // scoped enum is one that is not implicitly convertible to its underlying type.
89 //
90 // TODO: Replace with std::is_scoped_enum in C++23.
91 //
92 template <typename T, bool = std::is_enum_v<T>>
93 struct is_scoped_enum : std::false_type {};
94 
95 template <typename T>
96 struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
97 };
98 
99 template <typename T>
100 inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
101 
102 // Shorthand for casting an enumerator to its integral value.
103 //
104 // TODO: Replace with std::to_underlying in C++23.
105 //
106 //   enum class E { A, B, C };
107 //   static_assert(ftl::to_underlying(E::B) == 1);
108 //
109 template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>>
110 constexpr auto to_underlying(E v) {
111   return static_cast<std::underlying_type_t<E>>(v);
112 }
113 
114 // Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
115 // ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
116 // where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
117 // enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
118 // range values results in undefined behavior if the underlying type is not fixed.
119 //
120 //   enum class E { A, B, C, F = 5, ftl_last = F };
121 //
122 //   static_assert(ftl::enum_begin_v<E> == E::A);
123 //   static_assert(ftl::enum_last_v<E> == E::F);
124 //   static_assert(ftl::enum_size_v<E> == 6);
125 //
126 //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
127 //
128 //   static_assert(ftl::enum_begin_v<F> == F{0});
129 //   static_assert(ftl::enum_last_v<F> == F{15});
130 //   static_assert(ftl::enum_size_v<F> == 16);
131 //
132 template <typename E, typename = void>
133 struct enum_begin {
134   static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
135   static constexpr E value{0};
136 };
137 
138 template <typename E>
139 struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
140   static constexpr E value = E::ftl_first;
141 };
142 
143 template <typename E>
144 inline constexpr E enum_begin_v = enum_begin<E>::value;
145 
146 template <typename E, typename = void>
147 struct enum_end {
148   using U = std::underlying_type_t<E>;
149   static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
150 
151   static constexpr E value{std::numeric_limits<U>::digits};
152 };
153 
154 template <typename E>
155 struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
156   static constexpr E value = E{to_underlying(E::ftl_last) + 1};
157 };
158 
159 template <typename E>
160 inline constexpr E enum_end_v = enum_end<E>::value;
161 
162 template <typename E>
163 inline constexpr E enum_last_v = E{to_underlying(enum_end_v<E>) - 1};
164 
165 template <typename E>
166 struct enum_size {
167   static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
168   static constexpr auto kEnd = to_underlying(enum_end_v<E>);
169   static_assert(kBegin < kEnd, "Invalid range");
170 
171   static constexpr std::size_t value = kEnd - kBegin;
172   static_assert(value <= 64, "Excessive range size");
173 };
174 
175 template <typename E>
176 inline constexpr std::size_t enum_size_v = enum_size<E>::value;
177 
178 namespace details {
179 
180 template <auto V>
181 struct Identity {
182   static constexpr auto value = V;
183 };
184 
185 template <typename E>
186 using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
187 
188 template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
189 struct EnumRange;
190 
191 template <typename E, template <E> class F, typename T, T... Vs>
192 struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
193   static constexpr auto kBegin = to_underlying(enum_begin_v<E>);
194   static constexpr auto kSize = enum_size_v<E>;
195 
196   using R = decltype(F<E{}>::value);
197   const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
198 
199   constexpr const auto* begin() const { return values; }
200   constexpr const auto* end() const { return values + kSize; }
201 };
202 
203 template <auto V>
204 struct EnumName {
205   static constexpr auto value = ftl_enum<decltype(V), V>();
206 };
207 
208 template <auto V>
209 struct EnumNameFull {
210   static constexpr auto value = ftl_enum_full<decltype(V), V>();
211 };
212 
213 template <auto I>
214 struct FlagName {
215   using E = decltype(I);
216   using U = std::underlying_type_t<E>;
217 
218   static constexpr E V{U{1} << to_underlying(I)};
219   static constexpr auto value = ftl_enum<E, V>();
220 };
221 
222 }  // namespace details
223 
224 // Returns an iterable over the range of an enum.
225 //
226 //   enum class E { A, B, C, F = 5, ftl_last = F };
227 //
228 //   std::string string;
229 //   for (E v : ftl::enum_range<E>()) {
230 //     string += ftl::enum_name(v).value_or("?");
231 //   }
232 //
233 //   assert(string == "ABC??F");
234 //
235 template <typename E>
236 constexpr auto enum_range() {
237   return details::EnumRange<E>{};
238 }
239 
240 // Returns a stringified enumerator at compile time.
241 //
242 //   enum class E { A, B, C };
243 //   static_assert(ftl::enum_name<E::B>() == "B");
244 //
245 template <auto V>
246 constexpr std::string_view enum_name() {
247   constexpr auto kName = ftl_enum<decltype(V), V>();
248   static_assert(kName, "Unknown enumerator");
249   return *kName;
250 }
251 
252 // Returns a stringified enumerator with class at compile time.
253 //
254 //   enum class E { A, B, C };
255 //   static_assert(ftl::enum_name<E::B>() == "E::B");
256 //
257 template <auto V>
258 constexpr std::string_view enum_name_full() {
259   constexpr auto kName = ftl_enum_full<decltype(V), V>();
260   static_assert(kName, "Unknown enumerator");
261   return *kName;
262 }
263 
264 // Returns a stringified enumerator, possibly at compile time.
265 //
266 //   enum class E { A, B, C, F = 5, ftl_last = F };
267 //
268 //   static_assert(ftl::enum_name(E::C).value_or("?") == "C");
269 //   static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
270 //
271 template <typename E>
272 constexpr std::optional<std::string_view> enum_name(E v) {
273   const auto value = to_underlying(v);
274 
275   constexpr auto kBegin = to_underlying(enum_begin_v<E>);
276   constexpr auto kLast = to_underlying(enum_last_v<E>);
277   if (value < kBegin || value > kLast) return {};
278 
279   constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
280   return kRange.values[value - kBegin];
281 }
282 
283 // Returns a stringified enumerator with class, possibly at compile time.
284 //
285 //   enum class E { A, B, C, F = 5, ftl_last = F };
286 //
287 //   static_assert(ftl::enum_name(E::C).value_or("?") == "E::C");
288 //   static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
289 //
290 template <typename E>
291 constexpr std::optional<std::string_view> enum_name_full(E v) {
292   const auto value = to_underlying(v);
293 
294   constexpr auto kBegin = to_underlying(enum_begin_v<E>);
295   constexpr auto kLast = to_underlying(enum_last_v<E>);
296   if (value < kBegin || value > kLast) return {};
297 
298   constexpr auto kRange = details::EnumRange<E, details::EnumNameFull>{};
299   return kRange.values[value - kBegin];
300 }
301 
302 // Returns a stringified flag enumerator, possibly at compile time.
303 //
304 //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
305 //
306 //   static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
307 //   static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
308 //
309 template <typename E>
310 constexpr std::optional<std::string_view> flag_name(E v) {
311   const auto value = to_underlying(v);
312 
313   // TODO: Replace with std::popcount and std::countr_zero in C++20.
314   if (__builtin_popcountll(value) != 1) return {};
315 
316   constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
317   return kRange.values[__builtin_ctzll(value)];
318 }
319 
320 // Returns a stringified enumerator, or its integral value if not named.
321 //
322 //   enum class E { A, B, C, F = 5, ftl_last = F };
323 //
324 //   assert(ftl::enum_string(E::C) == "C");
325 //   assert(ftl::enum_string(E{3}) == "3");
326 //
327 template <typename E>
328 inline std::string enum_string(E v) {
329   if (const auto name = enum_name(v)) {
330     return std::string(*name);
331   }
332   return to_string(to_underlying(v));
333 }
334 
335 // Returns a stringified enumerator with class, or its integral value if not named.
336 //
337 //   enum class E { A, B, C, F = 5, ftl_last = F };
338 //
339 //   assert(ftl::enum_string(E::C) == "E::C");
340 //   assert(ftl::enum_string(E{3}) == "3");
341 //
342 template <typename E>
343 inline std::string enum_string_full(E v) {
344   if (const auto name = enum_name_full(v)) {
345       return std::string(*name);
346   }
347   return to_string(to_underlying(v));
348 }
349 
350 // Returns a stringified flag enumerator, or its integral value if not named.
351 //
352 //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
353 //
354 //   assert(ftl::flag_string(F::Z) == "Z");
355 //   assert(ftl::flag_string(F{7}) == "0b111");
356 //
357 template <typename E>
358 inline std::string flag_string(E v) {
359   if (const auto name = flag_name(v)) {
360     return std::string(*name);
361   }
362   constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
363   return to_string(to_underlying(v), radix);
364 }
365 
366 }  // namespace android::ftl
367