|  | /* | 
|  | * Copyright 2021 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <limits> | 
|  | #include <optional> | 
|  | #include <string_view> | 
|  | #include <type_traits> | 
|  | #include <utility> | 
|  |  | 
|  | #include <ftl/string.h> | 
|  |  | 
|  | // Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> by parsing the | 
|  | // compiler-generated string literal for the signature of this function. The function is defined in | 
|  | // the global namespace with a short name and inferred return type to reduce bloat in the read-only | 
|  | // data segment. | 
|  | template <typename E, E V> | 
|  | constexpr auto ftl_enum() { | 
|  | static_assert(std::is_enum_v<E>); | 
|  |  | 
|  | using R = std::optional<std::string_view>; | 
|  | using namespace std::literals; | 
|  |  | 
|  | // The "pretty" signature has the following format: | 
|  | // | 
|  | //   auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue] | 
|  | // | 
|  | std::string_view view = __PRETTY_FUNCTION__; | 
|  | const auto template_begin = view.rfind('['); | 
|  | const auto template_end = view.rfind(']'); | 
|  | if (template_begin == view.npos || template_end == view.npos) return R{}; | 
|  |  | 
|  | // Extract the template parameters without the enclosing brackets. Example (cont'd): | 
|  | // | 
|  | //   E = android::test::Enum, V = android::test::Enum::kValue | 
|  | // | 
|  | view = view.substr(template_begin + 1, template_end - template_begin - 1); | 
|  | const auto value_begin = view.rfind("V = "sv); | 
|  | if (value_begin == view.npos) return R{}; | 
|  |  | 
|  | // Example (cont'd): | 
|  | // | 
|  | //   V = android::test::Enum::kValue | 
|  | // | 
|  | view = view.substr(value_begin); | 
|  | const auto name_begin = view.rfind("::"sv); | 
|  | if (name_begin == view.npos) return R{}; | 
|  |  | 
|  | // Chop off the leading "::". | 
|  | const auto name = view.substr(name_begin + 2); | 
|  |  | 
|  | // A value that is not enumerated has the format "Enum)42". | 
|  | return name.find(')') == view.npos ? R{name} : R{}; | 
|  | } | 
|  |  | 
|  | namespace android::ftl { | 
|  |  | 
|  | // Trait for determining whether a type is specifically a scoped enum or not. By definition, a | 
|  | // scoped enum is one that is not implicitly convertible to its underlying type. | 
|  | // | 
|  | // TODO: Replace with std::is_scoped_enum in C++23. | 
|  | // | 
|  | template <typename T, bool = std::is_enum_v<T>> | 
|  | struct is_scoped_enum : std::false_type {}; | 
|  |  | 
|  | template <typename T> | 
|  | struct is_scoped_enum<T, true> : std::negation<std::is_convertible<T, std::underlying_type_t<T>>> { | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; | 
|  |  | 
|  | // Shorthand for casting an enumerator to its integral value. | 
|  | // | 
|  | //   enum class E { A, B, C }; | 
|  | //   static_assert(ftl::enum_cast(E::B) == 1); | 
|  | // | 
|  | template <typename E> | 
|  | constexpr auto enum_cast(E v) { | 
|  | return static_cast<std::underlying_type_t<E>>(v); | 
|  | } | 
|  |  | 
|  | // Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named | 
|  | // ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1 | 
|  | // where N is the bit width of the underlying type, but only if that type is unsigned, assuming the | 
|  | // enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of- | 
|  | // range values results in undefined behavior if the underlying type is not fixed. | 
|  | // | 
|  | //   enum class E { A, B, C, F = 5, ftl_last = F }; | 
|  | // | 
|  | //   static_assert(ftl::enum_begin_v<E> == E::A); | 
|  | //   static_assert(ftl::enum_last_v<E> == E::F); | 
|  | //   static_assert(ftl::enum_size_v<E> == 6); | 
|  | // | 
|  | //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; | 
|  | // | 
|  | //   static_assert(ftl::enum_begin_v<F> == F{0}); | 
|  | //   static_assert(ftl::enum_last_v<F> == F{15}); | 
|  | //   static_assert(ftl::enum_size_v<F> == 16); | 
|  | // | 
|  | template <typename E, typename = void> | 
|  | struct enum_begin { | 
|  | static_assert(is_scoped_enum_v<E>, "Missing ftl_first enumerator"); | 
|  | static constexpr E value{0}; | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | struct enum_begin<E, std::void_t<decltype(E::ftl_first)>> { | 
|  | static constexpr E value = E::ftl_first; | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | inline constexpr E enum_begin_v = enum_begin<E>::value; | 
|  |  | 
|  | template <typename E, typename = void> | 
|  | struct enum_end { | 
|  | using U = std::underlying_type_t<E>; | 
|  | static_assert(is_scoped_enum_v<E> && std::is_unsigned_v<U>, "Missing ftl_last enumerator"); | 
|  |  | 
|  | static constexpr E value{std::numeric_limits<U>::digits}; | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | struct enum_end<E, std::void_t<decltype(E::ftl_last)>> { | 
|  | static constexpr E value = E{enum_cast(E::ftl_last) + 1}; | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | inline constexpr E enum_end_v = enum_end<E>::value; | 
|  |  | 
|  | template <typename E> | 
|  | inline constexpr E enum_last_v = E{enum_cast(enum_end_v<E>) - 1}; | 
|  |  | 
|  | template <typename E> | 
|  | struct enum_size { | 
|  | static constexpr auto kBegin = enum_cast(enum_begin_v<E>); | 
|  | static constexpr auto kEnd = enum_cast(enum_end_v<E>); | 
|  | static_assert(kBegin < kEnd, "Invalid range"); | 
|  |  | 
|  | static constexpr std::size_t value = kEnd - kBegin; | 
|  | static_assert(value <= 64, "Excessive range size"); | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | inline constexpr std::size_t enum_size_v = enum_size<E>::value; | 
|  |  | 
|  | namespace details { | 
|  |  | 
|  | template <auto V> | 
|  | struct Identity { | 
|  | static constexpr auto value = V; | 
|  | }; | 
|  |  | 
|  | template <typename E> | 
|  | using make_enum_sequence = std::make_integer_sequence<std::underlying_type_t<E>, enum_size_v<E>>; | 
|  |  | 
|  | template <typename E, template <E> class = Identity, typename = make_enum_sequence<E>> | 
|  | struct EnumRange; | 
|  |  | 
|  | template <typename E, template <E> class F, typename T, T... Vs> | 
|  | struct EnumRange<E, F, std::integer_sequence<T, Vs...>> { | 
|  | static constexpr auto kBegin = enum_cast(enum_begin_v<E>); | 
|  | static constexpr auto kSize = enum_size_v<E>; | 
|  |  | 
|  | using R = decltype(F<E{}>::value); | 
|  | const R values[kSize] = {F<static_cast<E>(Vs + kBegin)>::value...}; | 
|  |  | 
|  | constexpr const auto* begin() const { return values; } | 
|  | constexpr const auto* end() const { return values + kSize; } | 
|  | }; | 
|  |  | 
|  | template <auto V> | 
|  | struct EnumName { | 
|  | static constexpr auto value = ftl_enum<decltype(V), V>(); | 
|  | }; | 
|  |  | 
|  | template <auto I> | 
|  | struct FlagName { | 
|  | using E = decltype(I); | 
|  | using U = std::underlying_type_t<E>; | 
|  |  | 
|  | static constexpr E V{U{1} << enum_cast(I)}; | 
|  | static constexpr auto value = ftl_enum<E, V>(); | 
|  | }; | 
|  |  | 
|  | }  // namespace details | 
|  |  | 
|  | // Returns an iterable over the range of an enum. | 
|  | // | 
|  | //   enum class E { A, B, C, F = 5, ftl_last = F }; | 
|  | // | 
|  | //   std::string string; | 
|  | //   for (E v : ftl::enum_range<E>()) { | 
|  | //     string += ftl::enum_name(v).value_or("?"); | 
|  | //   } | 
|  | // | 
|  | //   assert(string == "ABC??F"); | 
|  | // | 
|  | template <typename E> | 
|  | constexpr auto enum_range() { | 
|  | return details::EnumRange<E>{}; | 
|  | } | 
|  |  | 
|  | // Returns a stringified enumerator at compile time. | 
|  | // | 
|  | //   enum class E { A, B, C }; | 
|  | //   static_assert(ftl::enum_name<E::B>() == "B"); | 
|  | // | 
|  | template <auto V> | 
|  | constexpr std::string_view enum_name() { | 
|  | constexpr auto kName = ftl_enum<decltype(V), V>(); | 
|  | static_assert(kName, "Unknown enumerator"); | 
|  | return *kName; | 
|  | } | 
|  |  | 
|  | // Returns a stringified enumerator, possibly at compile time. | 
|  | // | 
|  | //   enum class E { A, B, C, F = 5, ftl_last = F }; | 
|  | // | 
|  | //   static_assert(ftl::enum_name(E::C).value_or("?") == "C"); | 
|  | //   static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); | 
|  | // | 
|  | template <typename E> | 
|  | constexpr std::optional<std::string_view> enum_name(E v) { | 
|  | const auto value = enum_cast(v); | 
|  |  | 
|  | constexpr auto kBegin = enum_cast(enum_begin_v<E>); | 
|  | constexpr auto kLast = enum_cast(enum_last_v<E>); | 
|  | if (value < kBegin || value > kLast) return {}; | 
|  |  | 
|  | constexpr auto kRange = details::EnumRange<E, details::EnumName>{}; | 
|  | return kRange.values[value - kBegin]; | 
|  | } | 
|  |  | 
|  | // Returns a stringified flag enumerator, possibly at compile time. | 
|  | // | 
|  | //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; | 
|  | // | 
|  | //   static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); | 
|  | //   static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); | 
|  | // | 
|  | template <typename E> | 
|  | constexpr std::optional<std::string_view> flag_name(E v) { | 
|  | const auto value = enum_cast(v); | 
|  |  | 
|  | // TODO: Replace with std::popcount and std::countr_zero in C++20. | 
|  | if (__builtin_popcountl(value) != 1) return {}; | 
|  |  | 
|  | constexpr auto kRange = details::EnumRange<E, details::FlagName>{}; | 
|  | return kRange.values[__builtin_ctzl(value)]; | 
|  | } | 
|  |  | 
|  | // Returns a stringified enumerator, or its integral value if not named. | 
|  | // | 
|  | //   enum class E { A, B, C, F = 5, ftl_last = F }; | 
|  | // | 
|  | //   assert(ftl::enum_string(E::C) == "C"); | 
|  | //   assert(ftl::enum_string(E{3}) == "3"); | 
|  | // | 
|  | template <typename E> | 
|  | inline std::string enum_string(E v) { | 
|  | if (const auto name = enum_name(v)) { | 
|  | return std::string(*name); | 
|  | } | 
|  | return to_string(enum_cast(v)); | 
|  | } | 
|  |  | 
|  | // Returns a stringified flag enumerator, or its integral value if not named. | 
|  | // | 
|  | //   enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; | 
|  | // | 
|  | //   assert(ftl::flag_string(F::Z) == "Z"); | 
|  | //   assert(ftl::flag_string(F{7}) == "0b111"); | 
|  | // | 
|  | template <typename E> | 
|  | inline std::string flag_string(E v) { | 
|  | if (const auto name = flag_name(v)) { | 
|  | return std::string(*name); | 
|  | } | 
|  | constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; | 
|  | return to_string(enum_cast(v), radix); | 
|  | } | 
|  |  | 
|  | }  // namespace android::ftl |