| // 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 <ostream> |
| #include <type_traits> |
| |
| #include "src/logging.h" |
| |
| namespace cobalt::util { |
| |
| template <class T> |
| class PinnedUniquePtr; |
| |
| // 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); |
| |
| // 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; |
| |
| // 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<NotNullUniquePtr<T>>::value, |
| "NotNullUniquePtr must not be copy constructible."); |
| static_assert(!std::is_copy_assignable<NotNullUniquePtr<T>>::value, |
| "NotNullUniquePtr must not be copy assignable."); |
| static_assert(!std::is_default_constructible<NotNullUniquePtr<T>>::value, |
| "NotNullUniquePtr must have no default constructor."); |
| static_assert(std::is_move_constructible<NotNullUniquePtr<T>>::value, |
| "NotNullUniquePtr must be move constructible."); |
| static_assert(std::is_move_assignable<NotNullUniquePtr<T>>::value, |
| "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_)) {} |
| |
| 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 is a hack to allow a test to keep a reference to a pointer while passing |
| // a NotNullUniquePtr to another class. It is currently only used in testing Cobalt 1.0 local |
| // aggregation and is not the preferred solution. |
| 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)); |
| } |
| |
| // 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> |
| explicit PinnedUniquePtr(NotNullUniquePtr<U>&& other) : 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(sizeof(std::unique_ptr<T>) == sizeof(PinnedUniquePtr<T>), |
| "PinnedUniquePtr must have zero space overhead."); |
| static_assert(!std::is_copy_constructible<PinnedUniquePtr<T>>::value, |
| "PinnedUniquePtr must not be copy constructible."); |
| static_assert(!std::is_copy_assignable<PinnedUniquePtr<T>>::value, |
| "PinnedUniquePtr must not be copy assignable."); |
| static_assert(!std::is_default_constructible<PinnedUniquePtr<T>>::value, |
| "PinnedUniquePtr must have no default constructor."); |
| static_assert(!std::is_move_constructible<PinnedUniquePtr<T>>::value, |
| "PinnedUniquePtr must not be move constructible."); |
| static_assert(!std::is_move_assignable<PinnedUniquePtr<T>>::value, |
| "PinnedUniquePtr must not be move assignable."); |
| } |
| |
| 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->() { return ptr_.operator->(); } |
| T& operator*() { return ptr_.operator*(); } |
| |
| private: |
| std::unique_ptr<T> ptr_; |
| |
| // Disable copy and move |
| PinnedUniquePtr() = delete; |
| PinnedUniquePtr(PinnedUniquePtr const&) = delete; |
| PinnedUniquePtr(PinnedUniquePtr&&) = delete; |
| }; |
| |
| } // namespace cobalt::util |
| |
| #endif // COBALT_SRC_LIB_UTIL_NOT_NULL_H_ |