blob: dfe3a0976bc42e36ee89e024db162e3be9663318 [file] [log] [blame]
Dominik Laskowski75788452021-02-09 18:51:25 -08001/*
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.
32template <typename E, E V>
33constexpr 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
71namespace 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//
78template <typename T, bool = std::is_enum_v<T>>
79struct is_scoped_enum : std::false_type {};
80
81template <typename T>
82struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> {
83};
84
85template <typename T>
86inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
87
88// Shorthand for casting an enumerator to its integral value.
89//
90// enum class E { A, B, C };
91// static_assert(ftl::enum_cast(E::B) == 1);
92//
93template <typename E>
94constexpr auto enum_cast(E v) {
95 return static_cast<std::underlying_type_t<E>>(v);
96}
97
98// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named
99// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1
100// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the
101// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of-
102// range values results in undefined behavior if the underlying type is not fixed.
103//
104// enum class E { A, B, C, F = 5, ftl_last = F };
105//
106// static_assert(ftl::enum_begin_v<E> == E::A);
107// static_assert(ftl::enum_last_v<E> == E::F);
108// static_assert(ftl::enum_size_v<E> == 6);
109//
110// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
111//
112// static_assert(ftl::enum_begin_v<F> == F{0});
113// static_assert(ftl::enum_last_v<F> == F{15});
114// static_assert(ftl::enum_size_v<F> == 16);
115//
116template <typename E, typename = void>
117struct enum_begin {
118 static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator");
119 static constexpr E value{0};
120};
121
122template <typename E>
123struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> {
124 static constexpr E value = E::ftl_first;
125};
126
127template <typename E>
128inline constexpr E enum_begin_v = enum_begin<E>::value;
129
130template <typename E, typename = void>
131struct enum_end {
132 using U = std::underlying_type_t<E>;
133 static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator");
134
135 static constexpr E value{std::numeric_limits<U>::digits};
136};
137
138template <typename E>
139struct enum_end<E, std::void_t<decltype(E::ftl_last)>> {
140 static constexpr E value = E{enum_cast(E::ftl_last) + 1};
141};
142
143template <typename E>
144inline constexpr E enum_end_v = enum_end<E>::value;
145
146template <typename E>
147inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1};
148
149template <typename E>
150struct enum_size {
151 static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
152 static constexpr auto kEnd = enum_cast(enum_end_v<E>);
153 static_assert(kBegin < kEnd, "Invalid range");
154
155 static constexpr std::size_t value = kEnd - kBegin;
156 static_assert(value <= 64, "Excessive range size");
157};
158
159template <typename E>
160inline constexpr std::size_t enum_size_v = enum_size<E>::value;
161
162namespace details {
163
164template <auto V>
165struct Identity {
166 static constexpr auto value = V;
167};
168
169template <typename E>
170using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>;
171
172template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>>
173struct EnumRange;
174
175template <typename E, template <E> class F, typename T, T... Vs>
176struct EnumRange<E, F, std::integer_sequence<T, Vs...>> {
177 static constexpr auto kBegin = enum_cast(enum_begin_v<E>);
178 static constexpr auto kSize = enum_size_v<E>;
179
180 using R = decltype(F<E{}>::value);
181 const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...};
182
183 constexpr const auto* begin() const { return values; }
184 constexpr const auto* end() const { return values + kSize; }
185};
186
187template <auto V>
188struct EnumName {
189 static constexpr auto value = ftl_enum<decltype(V), V>();
190};
191
192template <auto I>
193struct FlagName {
194 using E = decltype(I);
195 using U = std::underlying_type_t<E>;
196
197 static constexpr E V{U{1} << enum_cast(I)};
198 static constexpr auto value = ftl_enum<E, V>();
199};
200
201} // namespace details
202
203// Returns an iterable over the range of an enum.
204//
205// enum class E { A, B, C, F = 5, ftl_last = F };
206//
207// std::string string;
208// for (E v : ftl::enum_range<E>()) {
209// string += ftl::enum_name(v).value_or("?");
210// }
211//
212// assert(string == "ABC??F");
213//
214template <typename E>
215constexpr auto enum_range() {
216 return details::EnumRange<E>{};
217}
218
219// Returns a stringified enumerator at compile time.
220//
221// enum class E { A, B, C };
222// static_assert(ftl::enum_name<E::B>() == "B");
223//
224template <auto V>
225constexpr std::string_view enum_name() {
226 constexpr auto kName = ftl_enum<decltype(V), V>();
227 static_assert(kName, "Unknown enumerator");
228 return *kName;
229}
230
231// Returns a stringified enumerator, possibly at compile time.
232//
233// enum class E { A, B, C, F = 5, ftl_last = F };
234//
235// static_assert(ftl::enum_name(E::C).value_or("?") == "C");
236// static_assert(ftl::enum_name(E{3}).value_or("?") == "?");
237//
238template <typename E>
239constexpr std::optional<std::string_view> enum_name(E v) {
240 const auto value = enum_cast(v);
241
242 constexpr auto kBegin = enum_cast(enum_begin_v<E>);
243 constexpr auto kLast = enum_cast(enum_last_v<E>);
244 if (value < kBegin || value > kLast) return {};
245
246 constexpr auto kRange = details::EnumRange<E, details::EnumName>{};
247 return kRange.values[value - kBegin];
248}
249
250// Returns a stringified flag enumerator, possibly at compile time.
251//
252// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
253//
254// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z");
255// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?");
256//
257template <typename E>
258constexpr std::optional<std::string_view> flag_name(E v) {
259 const auto value = enum_cast(v);
260
261 // TODO: Replace with std::popcount and std::countr_zero in C++20.
262 if (__builtin_popcountl(value) != 1) return {};
263
264 constexpr auto kRange = details::EnumRange<E, details::FlagName>{};
265 return kRange.values[__builtin_ctzl(value)];
266}
267
268// Returns a stringified enumerator, or its integral value if not named.
269//
270// enum class E { A, B, C, F = 5, ftl_last = F };
271//
272// assert(ftl::enum_string(E::C) == "C");
273// assert(ftl::enum_string(E{3}) == "3");
274//
275template <typename E>
276inline std::string enum_string(E v) {
277 if (const auto name = enum_name(v)) {
278 return std::string(*name);
279 }
280 return to_string(enum_cast(v));
281}
282
283// Returns a stringified flag enumerator, or its integral value if not named.
284//
285// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 };
286//
287// assert(ftl::flag_string(F::Z) == "Z");
288// assert(ftl::flag_string(F{7}) == "0b111");
289//
290template <typename E>
291inline std::string flag_string(E v) {
292 if (const auto name = flag_name(v)) {
293 return std::string(*name);
294 }
295 constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex;
296 return to_string(enum_cast(v), radix);
297}
298
299} // namespace android::ftl