Loading include/ftl/algorithm.h +12 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,18 @@ namespace android::ftl { // Determines if a container contains a value. This is a simplified version of the C++23 // std::ranges::contains function. // // const ftl::StaticVector vector = {1, 2, 3}; // assert(ftl::contains(vector, 1)); // // TODO: Remove in C++23. template <typename Container, typename Value> auto contains(const Container& container, const Value& value) -> bool { return std::find(container.begin(), container.end(), value) != container.end(); } // Adapter for std::find_if that converts the return value from iterator to optional. // // const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; Loading include/ftl/non_null.h +104 −8 Original line number Diff line number Diff line Loading @@ -68,26 +68,28 @@ class NonNull final { constexpr NonNull(const NonNull&) = default; constexpr NonNull& operator=(const NonNull&) = default; constexpr const Pointer& get() const { return pointer_; } constexpr explicit operator const Pointer&() const { return get(); } [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } // Move operations. These break the invariant, so care must be taken to avoid subsequent access. constexpr NonNull(NonNull&&) = default; constexpr NonNull& operator=(NonNull&&) = default; constexpr Pointer take() && { return std::move(pointer_); } constexpr explicit operator Pointer() && { return take(); } [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); } [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } // Dereferencing. constexpr decltype(auto) operator*() const { return *get(); } constexpr decltype(auto) operator->() const { return get(); } [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions // through the passkey idiom, for clear compilation errors. template <typename P> constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) { if (!pointer_) std::abort(); if (pointer_ == nullptr) std::abort(); } private: Loading @@ -98,11 +100,13 @@ class NonNull final { }; template <typename P> constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { [[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { using Passkey = typename NonNull<std::decay_t<P>>::Passkey; return {Passkey{}, std::forward<P>(pointer)}; } // NonNull<P> <=> NonNull<Q> template <typename P, typename Q> constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() == rhs.get(); Loading @@ -113,4 +117,96 @@ constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return !operator==(lhs, rhs); } template <typename P, typename Q> constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() < rhs.get(); } template <typename P, typename Q> constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() <= rhs.get(); } template <typename P, typename Q> constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() >= rhs.get(); } template <typename P, typename Q> constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() > rhs.get(); } // NonNull<P> <=> Q template <typename P, typename Q> constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() == rhs; } template <typename P, typename Q> constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() != rhs; } template <typename P, typename Q> constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() < rhs; } template <typename P, typename Q> constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() <= rhs; } template <typename P, typename Q> constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() >= rhs; } template <typename P, typename Q> constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() > rhs; } // P <=> NonNull<Q> template <typename P, typename Q> constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) { return lhs == rhs.get(); } template <typename P, typename Q> constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) { return lhs != rhs.get(); } template <typename P, typename Q> constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) { return lhs < rhs.get(); } template <typename P, typename Q> constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) { return lhs <= rhs.get(); } template <typename P, typename Q> constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) { return lhs >= rhs.get(); } template <typename P, typename Q> constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) { return lhs > rhs.get(); } } // namespace android::ftl // Specialize std::hash for ftl::NonNull<T> template <typename P> struct std::hash<android::ftl::NonNull<P>> { std::size_t operator()(const android::ftl::NonNull<P>& ptr) const { return std::hash<P>()(ptr.get()); } }; libs/ftl/algorithm_test.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,17 @@ namespace android::test { // Keep in sync with example usage in header file. TEST(Algorithm, Contains) { const ftl::StaticVector vector = {1, 2, 3}; EXPECT_TRUE(ftl::contains(vector, 1)); EXPECT_FALSE(ftl::contains(vector, 0)); EXPECT_TRUE(ftl::contains(vector, 2)); EXPECT_TRUE(ftl::contains(vector, 3)); EXPECT_FALSE(ftl::contains(vector, 4)); } // Keep in sync with example usage in header file. TEST(Algorithm, FindIf) { using namespace std::string_view_literals; Loading libs/ftl/non_null_test.cpp +85 −1 Original line number Diff line number Diff line Loading @@ -14,12 +14,17 @@ * limitations under the License. */ #include <ftl/algorithm.h> #include <ftl/non_null.h> #include <gtest/gtest.h> #include <memory> #include <set> #include <string> #include <string_view> #include <type_traits> #include <unordered_set> #include <vector> namespace android::test { namespace { Loading Loading @@ -47,7 +52,7 @@ Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) { // Keep in sync with example usage in header file. TEST(NonNull, Example) { const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android")); std::size_t size; std::size_t size{}; get_length(string_ptr, ftl::as_non_null(&size)); EXPECT_EQ(size, 7u); Loading @@ -71,5 +76,84 @@ constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) { static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr); static_assert(static_cast<bool>(kApplePtr)); static_assert(std::is_same_v<decltype(ftl::as_non_null(std::declval<const int* const>())), ftl::NonNull<const int*>>); } // namespace TEST(NonNull, SwapRawPtr) { int i1 = 123; int i2 = 456; auto ptr1 = ftl::as_non_null(&i1); auto ptr2 = ftl::as_non_null(&i2); std::swap(ptr1, ptr2); EXPECT_EQ(*ptr1, 456); EXPECT_EQ(*ptr2, 123); } TEST(NonNull, SwapSmartPtr) { auto ptr1 = ftl::as_non_null(std::make_shared<int>(123)); auto ptr2 = ftl::as_non_null(std::make_shared<int>(456)); std::swap(ptr1, ptr2); EXPECT_EQ(*ptr1, 456); EXPECT_EQ(*ptr2, 123); } TEST(NonNull, VectorOfRawPtr) { int i = 1; std::vector<ftl::NonNull<int*>> vpi; vpi.push_back(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(vpi, nullptr)); EXPECT_TRUE(ftl::contains(vpi, &i)); EXPECT_TRUE(ftl::contains(vpi, vpi.front())); } TEST(NonNull, VectorOfSmartPtr) { std::vector<ftl::NonNull<std::shared_ptr<int>>> vpi; vpi.push_back(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(vpi, nullptr)); EXPECT_TRUE(ftl::contains(vpi, vpi.front().get())); EXPECT_TRUE(ftl::contains(vpi, vpi.front())); } TEST(NonNull, SetOfRawPtr) { int i = 1; std::set<ftl::NonNull<int*>> spi; spi.insert(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, &i)); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, SetOfSmartPtr) { std::set<ftl::NonNull<std::shared_ptr<int>>> spi; spi.insert(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, spi.begin()->get())); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, UnorderedSetOfRawPtr) { int i = 1; std::unordered_set<ftl::NonNull<int*>> spi; spi.insert(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, &i)); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, UnorderedSetOfSmartPtr) { std::unordered_set<ftl::NonNull<std::shared_ptr<int>>> spi; spi.insert(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, spi.begin()->get())); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } } // namespace android::test Loading
include/ftl/algorithm.h +12 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,18 @@ namespace android::ftl { // Determines if a container contains a value. This is a simplified version of the C++23 // std::ranges::contains function. // // const ftl::StaticVector vector = {1, 2, 3}; // assert(ftl::contains(vector, 1)); // // TODO: Remove in C++23. template <typename Container, typename Value> auto contains(const Container& container, const Value& value) -> bool { return std::find(container.begin(), container.end(), value) != container.end(); } // Adapter for std::find_if that converts the return value from iterator to optional. // // const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; Loading
include/ftl/non_null.h +104 −8 Original line number Diff line number Diff line Loading @@ -68,26 +68,28 @@ class NonNull final { constexpr NonNull(const NonNull&) = default; constexpr NonNull& operator=(const NonNull&) = default; constexpr const Pointer& get() const { return pointer_; } constexpr explicit operator const Pointer&() const { return get(); } [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } // Move operations. These break the invariant, so care must be taken to avoid subsequent access. constexpr NonNull(NonNull&&) = default; constexpr NonNull& operator=(NonNull&&) = default; constexpr Pointer take() && { return std::move(pointer_); } constexpr explicit operator Pointer() && { return take(); } [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); } [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } // Dereferencing. constexpr decltype(auto) operator*() const { return *get(); } constexpr decltype(auto) operator->() const { return get(); } [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions // through the passkey idiom, for clear compilation errors. template <typename P> constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) { if (!pointer_) std::abort(); if (pointer_ == nullptr) std::abort(); } private: Loading @@ -98,11 +100,13 @@ class NonNull final { }; template <typename P> constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { [[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { using Passkey = typename NonNull<std::decay_t<P>>::Passkey; return {Passkey{}, std::forward<P>(pointer)}; } // NonNull<P> <=> NonNull<Q> template <typename P, typename Q> constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() == rhs.get(); Loading @@ -113,4 +117,96 @@ constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return !operator==(lhs, rhs); } template <typename P, typename Q> constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() < rhs.get(); } template <typename P, typename Q> constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() <= rhs.get(); } template <typename P, typename Q> constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() >= rhs.get(); } template <typename P, typename Q> constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) { return lhs.get() > rhs.get(); } // NonNull<P> <=> Q template <typename P, typename Q> constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() == rhs; } template <typename P, typename Q> constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() != rhs; } template <typename P, typename Q> constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() < rhs; } template <typename P, typename Q> constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() <= rhs; } template <typename P, typename Q> constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() >= rhs; } template <typename P, typename Q> constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) { return lhs.get() > rhs; } // P <=> NonNull<Q> template <typename P, typename Q> constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) { return lhs == rhs.get(); } template <typename P, typename Q> constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) { return lhs != rhs.get(); } template <typename P, typename Q> constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) { return lhs < rhs.get(); } template <typename P, typename Q> constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) { return lhs <= rhs.get(); } template <typename P, typename Q> constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) { return lhs >= rhs.get(); } template <typename P, typename Q> constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) { return lhs > rhs.get(); } } // namespace android::ftl // Specialize std::hash for ftl::NonNull<T> template <typename P> struct std::hash<android::ftl::NonNull<P>> { std::size_t operator()(const android::ftl::NonNull<P>& ptr) const { return std::hash<P>()(ptr.get()); } };
libs/ftl/algorithm_test.cpp +11 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,17 @@ namespace android::test { // Keep in sync with example usage in header file. TEST(Algorithm, Contains) { const ftl::StaticVector vector = {1, 2, 3}; EXPECT_TRUE(ftl::contains(vector, 1)); EXPECT_FALSE(ftl::contains(vector, 0)); EXPECT_TRUE(ftl::contains(vector, 2)); EXPECT_TRUE(ftl::contains(vector, 3)); EXPECT_FALSE(ftl::contains(vector, 4)); } // Keep in sync with example usage in header file. TEST(Algorithm, FindIf) { using namespace std::string_view_literals; Loading
libs/ftl/non_null_test.cpp +85 −1 Original line number Diff line number Diff line Loading @@ -14,12 +14,17 @@ * limitations under the License. */ #include <ftl/algorithm.h> #include <ftl/non_null.h> #include <gtest/gtest.h> #include <memory> #include <set> #include <string> #include <string_view> #include <type_traits> #include <unordered_set> #include <vector> namespace android::test { namespace { Loading Loading @@ -47,7 +52,7 @@ Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) { // Keep in sync with example usage in header file. TEST(NonNull, Example) { const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android")); std::size_t size; std::size_t size{}; get_length(string_ptr, ftl::as_non_null(&size)); EXPECT_EQ(size, 7u); Loading @@ -71,5 +76,84 @@ constexpr StringViewPtr longest(StringViewPtr ptr1, StringViewPtr ptr2) { static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr); static_assert(static_cast<bool>(kApplePtr)); static_assert(std::is_same_v<decltype(ftl::as_non_null(std::declval<const int* const>())), ftl::NonNull<const int*>>); } // namespace TEST(NonNull, SwapRawPtr) { int i1 = 123; int i2 = 456; auto ptr1 = ftl::as_non_null(&i1); auto ptr2 = ftl::as_non_null(&i2); std::swap(ptr1, ptr2); EXPECT_EQ(*ptr1, 456); EXPECT_EQ(*ptr2, 123); } TEST(NonNull, SwapSmartPtr) { auto ptr1 = ftl::as_non_null(std::make_shared<int>(123)); auto ptr2 = ftl::as_non_null(std::make_shared<int>(456)); std::swap(ptr1, ptr2); EXPECT_EQ(*ptr1, 456); EXPECT_EQ(*ptr2, 123); } TEST(NonNull, VectorOfRawPtr) { int i = 1; std::vector<ftl::NonNull<int*>> vpi; vpi.push_back(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(vpi, nullptr)); EXPECT_TRUE(ftl::contains(vpi, &i)); EXPECT_TRUE(ftl::contains(vpi, vpi.front())); } TEST(NonNull, VectorOfSmartPtr) { std::vector<ftl::NonNull<std::shared_ptr<int>>> vpi; vpi.push_back(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(vpi, nullptr)); EXPECT_TRUE(ftl::contains(vpi, vpi.front().get())); EXPECT_TRUE(ftl::contains(vpi, vpi.front())); } TEST(NonNull, SetOfRawPtr) { int i = 1; std::set<ftl::NonNull<int*>> spi; spi.insert(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, &i)); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, SetOfSmartPtr) { std::set<ftl::NonNull<std::shared_ptr<int>>> spi; spi.insert(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, spi.begin()->get())); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, UnorderedSetOfRawPtr) { int i = 1; std::unordered_set<ftl::NonNull<int*>> spi; spi.insert(ftl::as_non_null(&i)); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, &i)); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } TEST(NonNull, UnorderedSetOfSmartPtr) { std::unordered_set<ftl::NonNull<std::shared_ptr<int>>> spi; spi.insert(ftl::as_non_null(std::make_shared<int>(2))); EXPECT_FALSE(ftl::contains(spi, nullptr)); EXPECT_TRUE(ftl::contains(spi, spi.begin()->get())); EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } } // namespace android::test