blob: b82c0339f8ccc5fcbf8b5c139e265343d1839587 [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 SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_BOX_H_
#define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_BOX_H_
#include <zircon/assert.h>
#include <memory>
namespace fidl {
namespace internal {
template <typename... T>
constexpr bool kAlwaysFalse = false;
} // namespace internal
// |Box<T>| is a wrapper around |std::unique_ptr<T>| that changes the behavior
// of `operator==` from pointer equality to value equality:
//
// - If one box holds a value while the other doesn't, they are not equal.
// - If both boxes don't hold values, they are equal.
// - Otherwise, delegate to `operator==` of |T|.
//
// |Box<T>| can be implicitly converted from an |std::unique_ptr<T>|.
// The primary purpose is that users will be able to use |std::unique_ptr<T>|
// in their application, and assign their data into natural domain objects
// with minimal syntax burden:
//
// fidl::Box<T> box = std::make_unique<T>(...);
//
// |Box<T>| will be used to express optionality in the natural domain objects in
// cases where the value need to be stored on the heap to break reference cycles
// from recursively referencing types. Overall, this makes `operator==` the
// standard way to compare objects for deep equality in FIDL types.
template <typename T>
class Box : private std::unique_ptr<T> {
public:
constexpr Box() noexcept = default;
~Box() = default;
constexpr Box(Box&& other) noexcept = default;
constexpr Box& operator=(Box&& other) noexcept = default;
// Support for directly assigning |unique_ptr|s into natural domain object
// fields which are |Box|es.
//
// NOLINTNEXTLINE
constexpr Box(std::unique_ptr<T> ptr) noexcept : std::unique_ptr<T>(std::move(ptr)) {}
using std::unique_ptr<T>::unique_ptr;
using std::unique_ptr<T>::operator=;
// Commonly used operations from |std::unique_ptr<T>|.
using std::unique_ptr<T>::operator*;
using std::unique_ptr<T>::operator->;
using std::unique_ptr<T>::operator bool;
using std::unique_ptr<T>::reset;
// A std::optional-like API:
bool has_value() const { return std::unique_ptr<T>::get(); }
const T& value() const& {
ZX_ASSERT(has_value());
return *std::unique_ptr<T>::get();
}
T& value() & {
ZX_ASSERT(has_value());
return *std::unique_ptr<T>::get();
}
template <class U>
constexpr T value_or(U&& default_value) const& {
if (has_value()) {
return value();
}
return default_value;
}
// Returns the wrapped |unique_ptr|.
std::unique_ptr<T>& unique_ptr() { return *this; }
const std::unique_ptr<T>& unique_ptr() const { return *this; }
};
template <typename T>
constexpr bool operator==(const Box<T>& lhs, const Box<T>& rhs) noexcept {
if (lhs) {
if (!rhs) {
return false;
}
return *lhs == *rhs;
}
if (rhs) {
return false;
}
return true;
}
template <typename T>
constexpr bool operator!=(const Box<T>& lhs, const Box<T>& rhs) noexcept {
return !(lhs == rhs);
}
template <typename T>
constexpr bool operator==(const std::nullptr_t& lhs, const Box<T>& rhs) noexcept {
return rhs.unique_ptr().get() == nullptr;
}
template <typename T>
constexpr bool operator!=(const std::nullptr_t& lhs, const Box<T>& rhs) noexcept {
return rhs.unique_ptr().get() != nullptr;
}
template <typename T>
constexpr bool operator==(const Box<T>& lhs, const std::nullptr_t& rhs) noexcept {
return lhs.unique_ptr().get() == nullptr;
}
template <typename T>
constexpr bool operator!=(const Box<T>& lhs, const std::nullptr_t& rhs) noexcept {
return lhs.unique_ptr().get() != nullptr;
}
template <typename T>
constexpr bool operator==(const Box<T>& lhs, const std::unique_ptr<T>& rhs) noexcept {
static_assert(internal::kAlwaysFalse<T>,
"Comparing a |fidl::Box<T>| and |std::unique_ptr<T>| is ambiguous. "
"|fidl::Box<T>| implements value equality while |std::unique_ptr<T>| implements "
"pointer equality.");
return false;
}
template <typename T>
constexpr bool operator==(const std::unique_ptr<T>& lhs, const Box<T>& rhs) noexcept {
static_assert(internal::kAlwaysFalse<T>,
"Comparing a |fidl::Box<T>| and |std::unique_ptr<T>| is ambiguous. "
"|fidl::Box<T>| implements value equality while |std::unique_ptr<T>| implements "
"pointer equality.");
return false;
}
template <typename T>
constexpr bool operator!=(const Box<T>& lhs, const std::unique_ptr<T>& rhs) noexcept {
static_assert(internal::kAlwaysFalse<T>,
"Comparing a |fidl::Box<T>| and |std::unique_ptr<T>| is ambiguous. "
"|fidl::Box<T>| implements value equality while |std::unique_ptr<T>| implements "
"pointer equality.");
return false;
}
template <typename T>
constexpr bool operator!=(const std::unique_ptr<T>& lhs, const Box<T>& rhs) noexcept {
static_assert(internal::kAlwaysFalse<T>,
"Comparing a |fidl::Box<T>| and |std::unique_ptr<T>| is ambiguous. "
"|fidl::Box<T>| implements value equality while |std::unique_ptr<T>| implements "
"pointer equality.");
return false;
}
} // namespace fidl
#endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_BOX_H_