diff options
author | 2024-10-02 13:44:41 -0700 | |
---|---|---|
committer | 2024-10-03 12:13:23 -0700 | |
commit | 6aa3b8931d7ffc8306b22358bb0dd4b2dc646b49 (patch) | |
tree | 48ae34d5846997541a6d36fa140ac7941f6c4226 | |
parent | 6d17c9b341b564f1e9e95dc477fb1681d6b3bb44 (diff) |
FTL: Allow implicit conversions with NonNull<T>
C++ allows certain implicit conversions for pointers, such as the
implicit conversion of "T*" to "const T*".
NonNull did not allow them, but that is easily corrected.
Bug: 185536303
Flag: EXEMPT Relaxes a compile-time constraint
Test: atest ftl_test
Change-Id: I0c14805c8fdcca979c67d2c86d57e5e678aa1b32
-rw-r--r-- | include/ftl/non_null.h | 9 | ||||
-rw-r--r-- | libs/ftl/non_null_test.cpp | 35 |
2 files changed, 44 insertions, 0 deletions
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h index 4a5d8bffd0..e68428810b 100644 --- a/include/ftl/non_null.h +++ b/include/ftl/non_null.h @@ -68,6 +68,15 @@ class NonNull final { constexpr NonNull(const NonNull&) = default; constexpr NonNull& operator=(const NonNull&) = default; + template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>> + constexpr NonNull(const NonNull<U>& other) : pointer_(other.get()) {} + + template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>> + constexpr NonNull& operator=(const NonNull<U>& other) { + pointer_ = other.get(); + return *this; + } + [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp index 367b398915..457237c35e 100644 --- a/libs/ftl/non_null_test.cpp +++ b/libs/ftl/non_null_test.cpp @@ -81,6 +81,31 @@ 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*>>); +class Base {}; +class Derived : public Base {}; + +static_assert(std::is_constructible_v<ftl::NonNull<void*>, ftl::NonNull<int*>>); +static_assert(!std::is_constructible_v<ftl::NonNull<int*>, ftl::NonNull<void*>>); +static_assert(std::is_constructible_v<ftl::NonNull<const int*>, ftl::NonNull<int*>>); +static_assert(!std::is_constructible_v<ftl::NonNull<int*>, ftl::NonNull<const int*>>); +static_assert(std::is_constructible_v<ftl::NonNull<Base*>, ftl::NonNull<Derived*>>); +static_assert(!std::is_constructible_v<ftl::NonNull<Derived*>, ftl::NonNull<Base*>>); +static_assert(std::is_constructible_v<ftl::NonNull<std::unique_ptr<const int>>, + ftl::NonNull<std::unique_ptr<int>>>); +static_assert(std::is_constructible_v<ftl::NonNull<std::unique_ptr<Base>>, + ftl::NonNull<std::unique_ptr<Derived>>>); + +static_assert(std::is_assignable_v<ftl::NonNull<void*>, ftl::NonNull<int*>>); +static_assert(!std::is_assignable_v<ftl::NonNull<int*>, ftl::NonNull<void*>>); +static_assert(std::is_assignable_v<ftl::NonNull<const int*>, ftl::NonNull<int*>>); +static_assert(!std::is_assignable_v<ftl::NonNull<int*>, ftl::NonNull<const int*>>); +static_assert(std::is_assignable_v<ftl::NonNull<Base*>, ftl::NonNull<Derived*>>); +static_assert(!std::is_assignable_v<ftl::NonNull<Derived*>, ftl::NonNull<Base*>>); +static_assert(std::is_assignable_v<ftl::NonNull<std::unique_ptr<const int>>, + ftl::NonNull<std::unique_ptr<int>>>); +static_assert(std::is_assignable_v<ftl::NonNull<std::unique_ptr<Base>>, + ftl::NonNull<std::unique_ptr<Derived>>>); + } // namespace TEST(NonNull, SwapRawPtr) { @@ -156,4 +181,14 @@ TEST(NonNull, UnorderedSetOfSmartPtr) { EXPECT_TRUE(ftl::contains(spi, *spi.begin())); } +TEST(NonNull, ImplicitConversion) { + int i = 123; + int j = 345; + auto ip = ftl::as_non_null(&i); + ftl::NonNull<void*> vp{ip}; + EXPECT_EQ(vp.get(), &i); + vp = ftl::as_non_null(&j); + EXPECT_EQ(vp.get(), &j); +} + } // namespace android::test |