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