blob: cc675bad44806e21b3e3994cff3f3b068216aae7 [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 <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_