Loading include/ftl/enum.h +72 −7 Original line number Diff line number Diff line Loading @@ -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>; Loading Loading @@ -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 "::". Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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 }; Loading @@ -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 }; Loading Loading @@ -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 }; Loading libs/ftl/enum_test.cpp +38 −0 Original line number Diff line number Diff line Loading @@ -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}); Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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); } } Loading @@ -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"); } } Loading Loading
include/ftl/enum.h +72 −7 Original line number Diff line number Diff line Loading @@ -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>; Loading Loading @@ -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 "::". Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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 }; Loading @@ -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 }; Loading Loading @@ -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 }; Loading
libs/ftl/enum_test.cpp +38 −0 Original line number Diff line number Diff line Loading @@ -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}); Loading Loading @@ -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, Loading @@ -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, Loading Loading @@ -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); } } Loading @@ -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"); } } Loading