blob: 4a3880303d84bcf0dfccae7e3f3a018203615ce6 [file] [log] [blame]
// 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_