Loading include/ftl/Flags.h +20 −79 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * Copyright 2020 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. Loading @@ -14,79 +14,22 @@ * limitations under the License. */ #include <android-base/stringprintf.h> #pragma once #include <ftl/enum.h> #include <ftl/string.h> #include <array> #include <cstdint> #include <optional> #include <iterator> #include <string> #include <type_traits> #include <ftl/NamedEnum.h> #include "utils/BitSet.h" #pragma once // TODO(b/185536303): Align with FTL style and namespace. namespace android { namespace details { template <typename F> inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; template <typename F, typename T, T... I> constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) { constexpr size_t count = seq.size(); std::array<F, count> values{}; for (size_t i = 0, v = 0; v < count; ++i) { values[v++] = static_cast<F>(T{1} << i); } return values; } template <typename F> inline constexpr auto flag_values = generate_flag_values<F>( std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{}); template <typename F, std::size_t... I> constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept { return std::array<std::optional<std::string_view>, sizeof...(I)>{ {enum_value_name<F, flag_values<F>[I]>()...}}; } template <typename F> inline constexpr auto flag_names = generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{}); // A trait for determining whether a type is specifically an enum class or not. template <typename T, bool = std::is_enum_v<T>> struct is_enum_class : std::false_type {}; // By definition, an enum class is an enum that is not implicitly convertible to its underlying // type. template <typename T> struct is_enum_class<T, true> : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {}; template <typename T> inline constexpr bool is_enum_class_v = is_enum_class<T>::value; } // namespace details template <auto V> constexpr auto flag_name() { using F = decltype(V); return details::enum_value_name<F, V>(); } template <typename F> constexpr std::optional<std::string_view> flag_name(F flag) { using U = std::underlying_type_t<F>; auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag))); return details::flag_names<F>[idx]; } /* A class for handling flags defined by an enum or enum class in a type-safe way. */ template <typename F> class Flags { Loading @@ -94,7 +37,7 @@ class Flags { // further to avoid this restriction but in general we want to encourage the use of enums // anyways. static_assert(std::is_enum_v<F>, "Flags type must be an enum"); using U = typename std::underlying_type_t<F>; using U = std::underlying_type_t<F>; public: constexpr Flags(F f) : mFlags(static_cast<U>(f)) {} Loading @@ -106,11 +49,10 @@ public: // should force them to be explicitly constructed from their underlying types to make full use // of the type checker. template <typename T = U> constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr) : mFlags(t) {} constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} template <typename T = U> explicit constexpr Flags(T t, typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr) explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} class Iterator { Loading Loading @@ -229,16 +171,16 @@ public: bool first = true; U unstringified = 0; for (const F f : *this) { std::optional<std::string_view> flagString = flag_name(f); if (flagString) { appendFlag(result, flagString.value(), first); if (const auto flagName = ftl::flag_name(f)) { appendFlag(result, flagName.value(), first); } else { unstringified |= static_cast<U>(f); } } if (unstringified != 0) { appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex; appendFlag(result, ftl::to_string(unstringified, radix), first); } if (first) { Loading @@ -265,15 +207,14 @@ private: // as flags. In order to use these, add them via a `using namespace` declaration. namespace flag_operators { template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> inline Flags<F> operator~(F f) { using U = typename std::underlying_type_t<F>; return static_cast<F>(~static_cast<U>(f)); return static_cast<F>(~ftl::enum_cast(f)); } template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> Flags<F> operator|(F lhs, F rhs) { using U = typename std::underlying_type_t<F>; return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs)); return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs)); } } // namespace flag_operators Loading include/ftl/NamedEnum.hdeleted 100644 → 0 +0 −129 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ #include <android-base/stringprintf.h> #include <array> #include <cstdint> #include <optional> #include <string> #pragma once namespace android { namespace details { template <typename E, E V> constexpr std::optional<std::string_view> enum_value_name() { // Should look something like (but all on one line): // std::optional<std::string_view> // android::details::enum_value_name() // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] std::string_view view = __PRETTY_FUNCTION__; size_t templateStart = view.rfind("["); size_t templateEnd = view.rfind("]"); if (templateStart == std::string::npos || templateEnd == std::string::npos) { return std::nullopt; } // Extract the template parameters without the enclosing braces. // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE view = view.substr(templateStart + 1, templateEnd - templateStart - 1); size_t valStart = view.rfind("V = "); if (valStart == std::string::npos) { return std::nullopt; } // Example (cont'd): V = android::test::TestEnums::ONE view = view.substr(valStart); // Check invalid enum values with cast, like V = (android::test::TestEnums)8. if (view.find('(') != std::string::npos) { return std::nullopt; } size_t nameStart = view.rfind("::"); if (nameStart == std::string::npos) { return std::nullopt; } // Chop off the initial "::" nameStart += 2; return view.substr(nameStart); } template <typename E, typename T, T... I> constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) { constexpr size_t count = seq.size(); std::array<E, count> values{}; for (size_t i = 0, v = 0; v < count; ++i) { values[v++] = static_cast<E>(T{0} + i); } return values; } template <typename E, std::size_t N> inline constexpr auto enum_values = generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{}); template <typename E, std::size_t N, std::size_t... I> constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept { return std::array<std::optional<std::string_view>, sizeof...(I)>{ {enum_value_name<E, enum_values<E, N>[I]>()...}}; } template <typename E, std::size_t N> inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{}); } // namespace details class NamedEnum { public: // By default allowed enum value range is 0 ~ 7. template <typename E> static constexpr size_t max = 8; template <auto V> static constexpr auto enum_name() { using E = decltype(V); return details::enum_value_name<E, V>(); } template <typename E> static constexpr std::optional<std::string_view> enum_name(E val) { auto idx = static_cast<size_t>(val); return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt; } // Helper function for parsing enum value to string. // Example : enum class TestEnums { ZERO = 0x0 }; // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, // it should be declared to specialized the maximum enum by below: // template <> constexpr size_t NamedEnum::max<TestEnums> = 16; // If the enum class definition is sparse and contains enum values starting from a large value, // Do not specialize it to a large number to avoid performance issues. // The recommended maximum enum number to specialize is 64. template <typename E> static const std::string string(E val, const char* fallbackFormat = "%02d") { std::string result; std::optional<std::string_view> enumString = enum_name(val); result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); return result; } }; } // namespace android include/ftl/enum.h 0 → 100644 +299 −0 Original line number Diff line number Diff line /* * 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 include/input/DisplayViewport.h +6 −4 Original line number Diff line number Diff line Loading @@ -18,7 +18,8 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include <android-base/stringprintf.h> #include <ftl/NamedEnum.h> #include <ftl/enum.h> #include <ftl/string.h> #include <gui/constants.h> #include <input/Input.h> Loading @@ -44,6 +45,8 @@ enum class ViewportType : int32_t { INTERNAL = 1, EXTERNAL = 2, VIRTUAL = 3, ftl_last = VIRTUAL }; /* Loading Loading @@ -132,9 +135,8 @@ struct DisplayViewport { "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d], " "isActive=[%d]", NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>", ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, deviceHeight, isActive); Loading include/input/InputDevice.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,9 @@ enum class InputDeviceSensorType : int32_t { GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, ftl_first = ACCELEROMETER, ftl_last = SIGNIFICANT_MOTION }; enum class InputDeviceSensorAccuracy : int32_t { Loading @@ -105,6 +108,8 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, RGB = 2, MULTI_COLOR = 3, ftl_last = MULTI_COLOR }; struct InputDeviceSensorInfo { Loading Loading
include/ftl/Flags.h +20 −79 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * Copyright 2020 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. Loading @@ -14,79 +14,22 @@ * limitations under the License. */ #include <android-base/stringprintf.h> #pragma once #include <ftl/enum.h> #include <ftl/string.h> #include <array> #include <cstdint> #include <optional> #include <iterator> #include <string> #include <type_traits> #include <ftl/NamedEnum.h> #include "utils/BitSet.h" #pragma once // TODO(b/185536303): Align with FTL style and namespace. namespace android { namespace details { template <typename F> inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; template <typename F, typename T, T... I> constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) { constexpr size_t count = seq.size(); std::array<F, count> values{}; for (size_t i = 0, v = 0; v < count; ++i) { values[v++] = static_cast<F>(T{1} << i); } return values; } template <typename F> inline constexpr auto flag_values = generate_flag_values<F>( std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{}); template <typename F, std::size_t... I> constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept { return std::array<std::optional<std::string_view>, sizeof...(I)>{ {enum_value_name<F, flag_values<F>[I]>()...}}; } template <typename F> inline constexpr auto flag_names = generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{}); // A trait for determining whether a type is specifically an enum class or not. template <typename T, bool = std::is_enum_v<T>> struct is_enum_class : std::false_type {}; // By definition, an enum class is an enum that is not implicitly convertible to its underlying // type. template <typename T> struct is_enum_class<T, true> : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {}; template <typename T> inline constexpr bool is_enum_class_v = is_enum_class<T>::value; } // namespace details template <auto V> constexpr auto flag_name() { using F = decltype(V); return details::enum_value_name<F, V>(); } template <typename F> constexpr std::optional<std::string_view> flag_name(F flag) { using U = std::underlying_type_t<F>; auto idx = static_cast<size_t>(__builtin_ctzl(static_cast<U>(flag))); return details::flag_names<F>[idx]; } /* A class for handling flags defined by an enum or enum class in a type-safe way. */ template <typename F> class Flags { Loading @@ -94,7 +37,7 @@ class Flags { // further to avoid this restriction but in general we want to encourage the use of enums // anyways. static_assert(std::is_enum_v<F>, "Flags type must be an enum"); using U = typename std::underlying_type_t<F>; using U = std::underlying_type_t<F>; public: constexpr Flags(F f) : mFlags(static_cast<U>(f)) {} Loading @@ -106,11 +49,10 @@ public: // should force them to be explicitly constructed from their underlying types to make full use // of the type checker. template <typename T = U> constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr) : mFlags(t) {} constexpr Flags(T t, std::enable_if_t<!ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} template <typename T = U> explicit constexpr Flags(T t, typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr) explicit constexpr Flags(T t, std::enable_if_t<ftl::is_scoped_enum_v<F>, T>* = nullptr) : mFlags(t) {} class Iterator { Loading Loading @@ -229,16 +171,16 @@ public: bool first = true; U unstringified = 0; for (const F f : *this) { std::optional<std::string_view> flagString = flag_name(f); if (flagString) { appendFlag(result, flagString.value(), first); if (const auto flagName = ftl::flag_name(f)) { appendFlag(result, flagName.value(), first); } else { unstringified |= static_cast<U>(f); } } if (unstringified != 0) { appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex; appendFlag(result, ftl::to_string(unstringified, radix), first); } if (first) { Loading @@ -265,15 +207,14 @@ private: // as flags. In order to use these, add them via a `using namespace` declaration. namespace flag_operators { template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> inline Flags<F> operator~(F f) { using U = typename std::underlying_type_t<F>; return static_cast<F>(~static_cast<U>(f)); return static_cast<F>(~ftl::enum_cast(f)); } template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> template <typename F, typename = std::enable_if_t<ftl::is_scoped_enum_v<F>>> Flags<F> operator|(F lhs, F rhs) { using U = typename std::underlying_type_t<F>; return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs)); return static_cast<F>(ftl::enum_cast(lhs) | ftl::enum_cast(rhs)); } } // namespace flag_operators Loading
include/ftl/NamedEnum.hdeleted 100644 → 0 +0 −129 Original line number Diff line number Diff line /* * Copyright (C) 2020 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. */ #include <android-base/stringprintf.h> #include <array> #include <cstdint> #include <optional> #include <string> #pragma once namespace android { namespace details { template <typename E, E V> constexpr std::optional<std::string_view> enum_value_name() { // Should look something like (but all on one line): // std::optional<std::string_view> // android::details::enum_value_name() // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] std::string_view view = __PRETTY_FUNCTION__; size_t templateStart = view.rfind("["); size_t templateEnd = view.rfind("]"); if (templateStart == std::string::npos || templateEnd == std::string::npos) { return std::nullopt; } // Extract the template parameters without the enclosing braces. // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE view = view.substr(templateStart + 1, templateEnd - templateStart - 1); size_t valStart = view.rfind("V = "); if (valStart == std::string::npos) { return std::nullopt; } // Example (cont'd): V = android::test::TestEnums::ONE view = view.substr(valStart); // Check invalid enum values with cast, like V = (android::test::TestEnums)8. if (view.find('(') != std::string::npos) { return std::nullopt; } size_t nameStart = view.rfind("::"); if (nameStart == std::string::npos) { return std::nullopt; } // Chop off the initial "::" nameStart += 2; return view.substr(nameStart); } template <typename E, typename T, T... I> constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) { constexpr size_t count = seq.size(); std::array<E, count> values{}; for (size_t i = 0, v = 0; v < count; ++i) { values[v++] = static_cast<E>(T{0} + i); } return values; } template <typename E, std::size_t N> inline constexpr auto enum_values = generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{}); template <typename E, std::size_t N, std::size_t... I> constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept { return std::array<std::optional<std::string_view>, sizeof...(I)>{ {enum_value_name<E, enum_values<E, N>[I]>()...}}; } template <typename E, std::size_t N> inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{}); } // namespace details class NamedEnum { public: // By default allowed enum value range is 0 ~ 7. template <typename E> static constexpr size_t max = 8; template <auto V> static constexpr auto enum_name() { using E = decltype(V); return details::enum_value_name<E, V>(); } template <typename E> static constexpr std::optional<std::string_view> enum_name(E val) { auto idx = static_cast<size_t>(val); return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt; } // Helper function for parsing enum value to string. // Example : enum class TestEnums { ZERO = 0x0 }; // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, // it should be declared to specialized the maximum enum by below: // template <> constexpr size_t NamedEnum::max<TestEnums> = 16; // If the enum class definition is sparse and contains enum values starting from a large value, // Do not specialize it to a large number to avoid performance issues. // The recommended maximum enum number to specialize is 64. template <typename E> static const std::string string(E val, const char* fallbackFormat = "%02d") { std::string result; std::optional<std::string_view> enumString = enum_name(val); result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); return result; } }; } // namespace android
include/ftl/enum.h 0 → 100644 +299 −0 Original line number Diff line number Diff line /* * 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
include/input/DisplayViewport.h +6 −4 Original line number Diff line number Diff line Loading @@ -18,7 +18,8 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include <android-base/stringprintf.h> #include <ftl/NamedEnum.h> #include <ftl/enum.h> #include <ftl/string.h> #include <gui/constants.h> #include <input/Input.h> Loading @@ -44,6 +45,8 @@ enum class ViewportType : int32_t { INTERNAL = 1, EXTERNAL = 2, VIRTUAL = 3, ftl_last = VIRTUAL }; /* Loading Loading @@ -132,9 +135,8 @@ struct DisplayViewport { "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d], " "isActive=[%d]", NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>", ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, deviceHeight, isActive); Loading
include/input/InputDevice.h +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,9 @@ enum class InputDeviceSensorType : int32_t { GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, ftl_first = ACCELEROMETER, ftl_last = SIGNIFICANT_MOTION }; enum class InputDeviceSensorAccuracy : int32_t { Loading @@ -105,6 +108,8 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, RGB = 2, MULTI_COLOR = 3, ftl_last = MULTI_COLOR }; struct InputDeviceSensorInfo { Loading