diff options
author | 2023-09-22 17:28:07 -0700 | |
---|---|---|
committer | 2023-09-23 00:35:57 +0000 | |
commit | aa68d0a1bafebd3e09f5cdd404b70e74b580578d (patch) | |
tree | 8e3b24b2df831d51836296ea4b757a014e274771 | |
parent | 290dc9350b2697f4c3e1bb5af82465a9d721395b (diff) |
ftl: add enum_string_full which includes the enum class name
Bug: 300701739
Test: presubmit
Change-Id: I235275b5d299efea779851434c392ca4e07ca4fd
-rw-r--r-- | include/ftl/enum.h | 79 | ||||
-rw-r--r-- | libs/ftl/enum_test.cpp | 38 |
2 files changed, 110 insertions, 7 deletions
diff --git a/include/ftl/enum.h b/include/ftl/enum.h index 075d12bd17..2c86e2e4c9 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -25,12 +25,12 @@ #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() { +// Returns the name of enumerator E::V and optionally the class (i.e. "E::V" or "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 <bool S, typename E, E V> +constexpr auto ftl_enum_builder() { static_assert(std::is_enum_v<E>); using R = std::optional<std::string_view>; @@ -58,7 +58,9 @@ constexpr auto ftl_enum() { // V = android::test::Enum::kValue // view = view.substr(value_begin); - const auto name_begin = view.rfind("::"sv); + const auto pos = S ? view.rfind("::"sv) - 2 : view.npos; + + const auto name_begin = view.rfind("::"sv, pos); if (name_begin == view.npos) return R{}; // Chop off the leading "::". @@ -68,6 +70,18 @@ constexpr auto ftl_enum() { return name.find(')') == view.npos ? R{name} : R{}; } +// Returns the name of enumerator E::V (i.e. "V") as std::optional<std::string_view> +template <typename E, E V> +constexpr auto ftl_enum() { + return ftl_enum_builder<false, E, V>(); +} + +// Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional<std::string_view> +template <typename E, E V> +constexpr auto ftl_enum_full() { + return ftl_enum_builder<true, E, V>(); +} + namespace android::ftl { // Trait for determining whether a type is specifically a scoped enum or not. By definition, a @@ -191,6 +205,11 @@ struct EnumName { static constexpr auto value = ftl_enum<decltype(V), V>(); }; +template <auto V> +struct EnumNameFull { + static constexpr auto value = ftl_enum_full<decltype(V), V>(); +}; + template <auto I> struct FlagName { using E = decltype(I); @@ -230,6 +249,18 @@ constexpr std::string_view enum_name() { return *kName; } +// Returns a stringified enumerator with class at compile time. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_name<E::B>() == "E::B"); +// +template <auto V> +constexpr std::string_view enum_name_full() { + constexpr auto kName = ftl_enum_full<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 }; @@ -249,6 +280,25 @@ constexpr std::optional<std::string_view> enum_name(E v) { return kRange.values[value - kBegin]; } +// Returns a stringified enumerator with class, possibly at compile time. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_name(E::C).value_or("?") == "E::C"); +// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +// +template <typename E> +constexpr std::optional<std::string_view> enum_name_full(E v) { + const auto value = to_underlying(v); + + constexpr auto kBegin = to_underlying(enum_begin_v<E>); + constexpr auto kLast = to_underlying(enum_last_v<E>); + if (value < kBegin || value > kLast) return {}; + + constexpr auto kRange = details::EnumRange<E, details::EnumNameFull>{}; + 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 }; @@ -282,6 +332,21 @@ inline std::string enum_string(E v) { return to_string(to_underlying(v)); } +// Returns a stringified enumerator with class, or its integral value if not named. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// assert(ftl::enum_string(E::C) == "E::C"); +// assert(ftl::enum_string(E{3}) == "3"); +// +template <typename E> +inline std::string enum_string_full(E v) { + if (const auto name = enum_name_full(v)) { + return std::string(*name); + } + return to_string(to_underlying(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 }; diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp index 5592a01fde..b68c2c3d02 100644 --- a/libs/ftl/enum_test.cpp +++ b/libs/ftl/enum_test.cpp @@ -33,6 +33,11 @@ static_assert(ftl::enum_name<E::ftl_last>() == "F"); static_assert(ftl::enum_name(E::C).value_or("?") == "C"); static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +static_assert(ftl::enum_name_full<E::B>() == "E::B"); +static_assert(ftl::enum_name_full<E::ftl_last>() == "E::F"); +static_assert(ftl::enum_name_full(E::C).value_or("?") == "E::C"); +static_assert(ftl::enum_name_full(E{3}).value_or("?") == "?"); + enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; static_assert(ftl::enum_begin_v<F> == F{0}); @@ -60,6 +65,10 @@ static_assert(ftl::enum_name<Flags::kNone>() == "kNone"); static_assert(ftl::enum_name<Flags::kFlag4>() == "kFlag4"); static_assert(ftl::enum_name<Flags::kFlag7>() == "kFlag7"); +static_assert(ftl::enum_name_full<Flags::kNone>() == "Flags::kNone"); +static_assert(ftl::enum_name_full<Flags::kFlag4>() == "Flags::kFlag4"); +static_assert(ftl::enum_name_full<Flags::kFlag7>() == "Flags::kFlag7"); + // Though not flags, the enumerators are within the implicit range of bit indices. enum class Planet : std::uint8_t { kMercury, @@ -81,6 +90,9 @@ static_assert(ftl::enum_size_v<Planet> == 8); static_assert(ftl::enum_name<Planet::kMercury>() == "kMercury"); static_assert(ftl::enum_name<Planet::kSaturn>() == "kSaturn"); +static_assert(ftl::enum_name_full<Planet::kMercury>() == "Planet::kMercury"); +static_assert(ftl::enum_name_full<Planet::kSaturn>() == "Planet::kSaturn"); + // Unscoped enum must define explicit range, even if the underlying type is fixed. enum Temperature : int { kRoom = 20, @@ -122,16 +134,28 @@ TEST(Enum, Name) { EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth"); EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune"); + EXPECT_EQ(ftl::enum_name_full(Planet::kEarth), "Planet::kEarth"); + EXPECT_EQ(ftl::enum_name_full(Planet::kNeptune), "Planet::kNeptune"); + EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(kPluto), std::nullopt); } { EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(-30)), std::nullopt); EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(0)), std::nullopt); EXPECT_EQ(ftl::enum_name(static_cast<Temperature>(100)), std::nullopt); + + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(-30)), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(0)), std::nullopt); + EXPECT_EQ(ftl::enum_name_full(static_cast<Temperature>(100)), std::nullopt); } } @@ -158,16 +182,30 @@ TEST(Enum, String) { EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth"); EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune"); + EXPECT_EQ(ftl::enum_string_full(Planet::kEarth), "Planet::kEarth"); + EXPECT_EQ(ftl::enum_string_full(Planet::kNeptune), "Planet::kNeptune"); + EXPECT_EQ(ftl::enum_string(kPluto), "8"); + + EXPECT_EQ(ftl::enum_string_full(kPluto), "8"); + } { EXPECT_EQ(ftl::enum_string(kRoom), "kRoom"); EXPECT_EQ(ftl::enum_string(kFridge), "kFridge"); EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer"); + EXPECT_EQ(ftl::enum_string_full(kRoom), "20"); + EXPECT_EQ(ftl::enum_string_full(kFridge), "4"); + EXPECT_EQ(ftl::enum_string_full(kFreezer), "-18"); + EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(-30)), "-30"); EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(0)), "0"); EXPECT_EQ(ftl::enum_string(static_cast<Temperature>(100)), "100"); + + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(-30)), "-30"); + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(0)), "0"); + EXPECT_EQ(ftl::enum_string_full(static_cast<Temperature>(100)), "100"); } } |