summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ftl/non_null.h112
-rw-r--r--libs/ftl/non_null_test.cpp86
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