diff options
-rw-r--r-- | include/ftl/non_null.h | 112 | ||||
-rw-r--r-- | libs/ftl/non_null_test.cpp | 86 |
2 files changed, 189 insertions, 9 deletions
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h index 35d09d71de..4a5d8bffd0 100644 --- a/include/ftl/non_null.h +++ b/include/ftl/non_null.h @@ -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: @@ -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(); @@ -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()); + } +}; diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp index bd0462b3b6..367b398915 100644 --- a/libs/ftl/non_null_test.cpp +++ b/libs/ftl/non_null_test.cpp @@ -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 { @@ -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); @@ -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 |