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