| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COBALT_SRC_LIB_UTIL_NOT_NULL_H_ |
| #define COBALT_SRC_LIB_UTIL_NOT_NULL_H_ |
| |
| #include <memory> |
| #include <type_traits> |
| |
| #include "src/logging.h" |
| #include "src/public/lib/statusor/statusor.h" |
| |
| namespace cobalt::util { |
| |
| // NotNullUniquePtr is a wrapper around std::unique_ptr<T> that guarantees that the contained type |
| // is non-null at construction time. |
| // |
| // It does this by: |
| // 1) Only being constructible by using MakeNotNullUniquePtr or WrapNotNullUniquePtrOrDefault |
| // which both guarantee that the contained type cannot be null. |
| // 2) Not allowing any access to the contained type. |
| // |
| // NotNullUniquePtr is a type that is only used in *transit*. It works as a return type to |
| // functions, and it can be transparently converted into a PinnedUniquePtr (see below). |
| template <class T> |
| class NotNullUniquePtr { |
| private: |
| template <typename U, class... Args> |
| friend constexpr NotNullUniquePtr<U> MakeNotNullUniquePtr(Args&&... args); |
| |
| template <typename U, typename V, class... Args> |
| friend constexpr NotNullUniquePtr<V> WrapNotNullUniquePtrOrDefault(std::unique_ptr<V>&& other, |
| Args&&... args); |
| |
| template <typename U> |
| friend constexpr NotNullUniquePtr<U> TESTONLY_TakeRawPointer(U* other); |
| |
| template <typename U> |
| friend constexpr lib::statusor::StatusOr<NotNullUniquePtr<U>> WrapNotNullUniquePtr( |
| std::unique_ptr<U>&& other); |
| |
| // PinnedUniquePtr is move-constructed from a NotNullUniquePtr, and thus needs to access the |
| // contained ptr_. |
| template <class U> |
| friend class PinnedUniquePtr; |
| |
| template <class U> |
| friend class NotNullUniquePtr; |
| |
| template <class U> |
| friend class NotNullSharedPtr; |
| |
| // Private constructor. ptr *must* be non null. |
| template <typename U> |
| explicit NotNullUniquePtr(std::unique_ptr<U> ptr) : ptr_(std::move(ptr)) { |
| // Invariants that must be true if we are to trust this class. |
| static_assert(sizeof(std::unique_ptr<T>) == sizeof(NotNullUniquePtr<T>), |
| "NotNullUniquePtr must have zero space overhead."); |
| static_assert(!std::is_copy_constructible_v<NotNullUniquePtr<T>>, |
| "NotNullUniquePtr must not be copy constructible."); |
| static_assert(!std::is_copy_assignable_v<NotNullUniquePtr<T>>, |
| "NotNullUniquePtr must not be copy assignable."); |
| static_assert(!std::is_default_constructible_v<NotNullUniquePtr<T>>, |
| "NotNullUniquePtr must have no default constructor."); |
| static_assert(std::is_move_constructible_v<NotNullUniquePtr<T>>, |
| "NotNullUniquePtr must be move constructible."); |
| static_assert(std::is_move_assignable_v<NotNullUniquePtr<T>>, |
| "NotNullUniquePtr must be move assignable."); |
| } |
| |
| public: |
| // Move constructor. Source NotNullUniquePtr will be invalidated. |
| template <typename U> |
| NotNullUniquePtr(NotNullUniquePtr<U>&& other) // NOLINT(google-explicit-constructor) |
| : ptr_(std::move(other.ptr_)) {} |
| |
| // Unwrap removes the inner unique_ptr from the NotNullUniquePtr. The NotNullUniquePtr is no |
| // longer valid after this call and must not be used again. |
| std::unique_ptr<T> Unwrap() && { return std::move(ptr_); } |
| |
| private: |
| std::unique_ptr<T> ptr_; |
| }; |
| |
| // MakeNotNullUniquePtr is equivalent to std::make_unique, and constructs a NotNullUniquePtr that |
| // is guaranteed to not be null. |
| template <typename T, class... Args> |
| constexpr NotNullUniquePtr<T> MakeNotNullUniquePtr(Args&&... args) { |
| return NotNullUniquePtr<T>(std::make_unique<T>(std::forward<Args>(args)...)); |
| } |
| |
| // WrapNotNullUniquePtrOrDefault allows converting std::unique_ptr into a NotNullUniquePtr. It |
| // does this by taking arguments for a fallback constructor after the std::unique_ptr argument. If |
| // the provided std::unique_ptr happens to be null, the fallback constructor will be used. This |
| // way the pointer is guaranteed never to be null. |
| template <typename T, typename U, class... Args> |
| constexpr NotNullUniquePtr<U> WrapNotNullUniquePtrOrDefault(std::unique_ptr<U>&& other, |
| Args&&... args) { |
| if (other) { |
| return NotNullUniquePtr<U>(std::move(other)); |
| } |
| return NotNullUniquePtr<U>(std::make_unique<T>(std::forward<Args>(args)...)); |
| } |
| |
| // TESTONLY_TakeRawPointer takes a (possible null) pointer to an object of type T. If the pointer |
| // is non-null, it will return a NotNullUniquePtr<T> that takes ownership of the provided pointer. |
| // Otherwise it will CHECK-fail. |
| template <typename T> |
| constexpr NotNullUniquePtr<T> TESTONLY_TakeRawPointer(T* other) { |
| CHECK(other) << "Expected non-null other in TESTONLY_TakeRawPointer"; |
| return NotNullUniquePtr<T>(std::unique_ptr<T>(other)); |
| } |
| |
| /// WrapNotNullUniquePtr takes a (possible null) pointer to an object of type T. If the pointer is |
| /// non-null, it will return a NotNullUniquePtr<T> that takes ownership of the provided pointer. |
| /// Otherwise it will return a non-OK status. |
| template <typename T> |
| constexpr lib::statusor::StatusOr<NotNullUniquePtr<T>> WrapNotNullUniquePtr( |
| std::unique_ptr<T>&& other) { |
| if (other) { |
| return NotNullUniquePtr<T>(std::move(other)); |
| } |
| return Status(StatusCode::FAILED_PRECONDITION, |
| "Pointer provided to WrapNotNullUniquePtr is null"); |
| } |
| |
| // PinnedUniquePtr is a wrapper around std::unique_ptr<T> that guarantees that the held pointer is |
| // never null. |
| // |
| // It does this by: |
| // 1) Only being constructible by a move-constructor from NotNullUniquePtr |
| // 2) Being immovable (since a move can invalidate the source ptr) |
| // |
| // PinnedUniquePtr is the actually usable part of the pair of classes in this file, allowing get, |
| // operator->, operator*, as well as swap() access. No other methods of the underlying unique_ptr |
| // are allowed, as they may cause the held pointer to become null. |
| template <class T> |
| class PinnedUniquePtr { |
| public: |
| // Swaps the managed objects and associated deleters of *this and another PinnedUniquePtr object |
| // *other*. |
| template <class U> |
| void swap(PinnedUniquePtr<U>& other) noexcept { |
| other.ptr_.swap(ptr_); |
| } |
| |
| // Swaps the managed objects and associated deleters of *this and a NotNullUniquePtr object. |
| template <class U> |
| void swap(NotNullUniquePtr<U>& other) noexcept { |
| other.ptr_.swap(ptr_); |
| } |
| |
| // Swaps the managed objects and associated deleters of *this and a NotNullUniquePtr object (by |
| // rvalue). |
| template <class U> |
| void swap(NotNullUniquePtr<U>&& other) noexcept { |
| other.ptr_.swap(ptr_); |
| } |
| |
| // Construct the PinnedUniquePtr from an existing NotNullUniquePtr. This invalidates the source |
| // ptr, and pins the managed object in place. |
| template <typename U> |
| PinnedUniquePtr(NotNullUniquePtr<U>&& other) // NOLINT(google-explicit-constructor) |
| : ptr_(std::move(other.ptr_)) { |
| // Invariants that must be true if we are to trust this class. |
| static_assert(sizeof(std::unique_ptr<T>) == sizeof(PinnedUniquePtr<T>), |
| "PinnedUniquePtr must have zero space overhead."); |
| static_assert(!std::is_copy_constructible_v<PinnedUniquePtr<T>>, |
| "PinnedUniquePtr must not be copy constructible."); |
| static_assert(!std::is_copy_assignable_v<PinnedUniquePtr<T>>, |
| "PinnedUniquePtr must not be copy assignable."); |
| static_assert(!std::is_default_constructible_v<PinnedUniquePtr<T>>, |
| "PinnedUniquePtr must have no default constructor."); |
| static_assert(!std::is_move_constructible_v<PinnedUniquePtr<T>>, |
| "PinnedUniquePtr must not be move constructible."); |
| static_assert(!std::is_move_assignable_v<PinnedUniquePtr<T>>, |
| "PinnedUniquePtr must not be move assignable."); |
| } |
| |
| template <typename U> |
| PinnedUniquePtr& operator=(NotNullUniquePtr<U>&& other) { |
| ptr_ = std::move(other.ptr_); |
| return *this; |
| } |
| |
| template <typename U> |
| PinnedUniquePtr& operator=(NotNullUniquePtr<U>& other) { |
| ptr_ = std::move(other.ptr_); |
| return *this; |
| } |
| |
| // Access the contained ptr. Guaranteed never to return null. |
| [[nodiscard]] T* get() const noexcept { return ptr_.get(); } |
| T* operator->() const noexcept { return ptr_.operator->(); } |
| T& operator*() const noexcept { return ptr_.operator*(); } |
| |
| private: |
| std::unique_ptr<T> ptr_; |
| |
| // Disable copy and move |
| PinnedUniquePtr() = delete; |
| PinnedUniquePtr(PinnedUniquePtr const&) = delete; |
| PinnedUniquePtr(PinnedUniquePtr&&) = delete; |
| }; |
| |
| template <class T> |
| class NotNullSharedPtr { |
| public: |
| template <typename U> |
| friend constexpr lib::statusor::StatusOr<NotNullSharedPtr<U>> MaybeWrapNotNullSharedPtr( |
| std::shared_ptr<U>&& other); |
| |
| // Construct the NotNullSharedPtr from an existing NotNullUniquePtr. This invalidates the source |
| // ptr, and pins the managed object in place. |
| template <typename U> |
| explicit NotNullSharedPtr(NotNullUniquePtr<U>&& other) : ptr_(std::move(other.ptr_)) { |
| // Invariants that must be true if we are to trust this class. |
| static_assert(sizeof(std::shared_ptr<T>) == sizeof(NotNullSharedPtr<T>), |
| "NotNullSharedPtr must have zero space overhead."); |
| static_assert(std::is_copy_constructible_v<NotNullSharedPtr<T>>, |
| "NotNullSharedPtr must be copy constructible."); |
| static_assert(std::is_copy_assignable_v<NotNullSharedPtr<T>>, |
| "NotNullSharedPtr must be copy assignable."); |
| static_assert(!std::is_default_constructible_v<NotNullSharedPtr<T>>, |
| "NotNullSharedPtr must have no default constructor."); |
| static_assert(std::is_move_constructible_v<NotNullSharedPtr<T>>, |
| "NotNullSharedPtr must be move constructible."); |
| static_assert(std::is_move_assignable_v<NotNullSharedPtr<T>>, |
| "NotNullSharedPtr must be move assignable."); |
| } |
| |
| template <typename U> |
| NotNullSharedPtr& operator=(NotNullUniquePtr<U>&& other) { |
| ptr_ = std::move(other.ptr_); |
| return *this; |
| } |
| |
| // Access the contained ptr. Guaranteed never to return null. |
| [[nodiscard]] T* get() const noexcept { return ptr_.get(); } |
| T* operator->() const noexcept { return ptr_.operator->(); } |
| T& operator*() const noexcept { return ptr_.operator*(); } |
| |
| private: |
| template <typename U> |
| explicit NotNullSharedPtr(std::shared_ptr<U> other) : ptr_(std::move(other)) {} |
| |
| std::shared_ptr<T> ptr_; |
| }; |
| |
| template <typename T> |
| constexpr lib::statusor::StatusOr<NotNullSharedPtr<T>> MaybeWrapNotNullSharedPtr( |
| std::shared_ptr<T>&& other) { |
| if (other) { |
| return NotNullSharedPtr<T>(std::move(other)); |
| } |
| return Status(StatusCode::FAILED_PRECONDITION, |
| "Pointer provided to MaybeWrapNotNullSharedPointer is null"); |
| } |
| |
| } // namespace cobalt::util |
| |
| #endif // COBALT_SRC_LIB_UTIL_NOT_NULL_H_ |