// Copyright 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Copyright 2018 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. #pragma once #include #include #include #include namespace android::base { namespace fit { // Determines whether a type can be compared with nullptr. template struct IsComparableWithNull : public std::false_type {}; template struct IsComparableWithNull() == nullptr)> : public std::true_type {}; // Suppress the warning when the compiler can see that a nullable value is // never equal to nullptr. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Waddress" template ::value, bool> = true> constexpr inline bool isNull(T&& value) { return std::forward(value) == nullptr; } #pragma GCC diagnostic pop template ::value, bool> = false> constexpr inline bool isNull(T&&) { return false; } // Determines whether a type can be initialized, assigned, and compared // with nullptr. template struct IsNullable : public std::integral_constant::value && std::is_assignable::value && IsComparableWithNull::value> {}; template <> struct IsNullable : public std::false_type {}; // Holds a value or nullptr. // // This class is similar to |std::optional| except that it uses less // storage when the value type can be initialized, assigned, and compared // with nullptr. // // For example: // - sizeof(fit::nullable) == sizeof(void*) // - sizeof(std::optional) == sizeof(struct { bool; void*; }) // - sizeof(fit::nullable) == sizeof(struct { bool; int; }) // - sizeof(std::optional) == sizeof(struct { bool; int; }) // // TODO(fxbug.dev/4681): fit::nullable does not precisely mirror // std::optional. This should be corrected to avoid surprises when switching // between the types. template ::value && std::is_constructible::value && std::is_assignable::value)> class Nullable final { public: using value_type = T; ~Nullable() = default; constexpr Nullable() = default; explicit constexpr Nullable(decltype(nullptr)) {} explicit constexpr Nullable(T value) : mOpt(std::move(value)) {} constexpr Nullable(const Nullable& other) = default; constexpr Nullable& operator=(const Nullable& other) = default; constexpr Nullable(Nullable&& other) = default; constexpr Nullable& operator=(Nullable&& other) = default; constexpr T& value() & { return mOpt.value(); } constexpr const T& value() const& { return mOpt.value(); } constexpr T&& value() && { return std::move(mOpt.value()); } constexpr const T&& value() const&& { return std::move(mOpt.value()); } template constexpr T valueOr(U&& default_value) const { return mOpt.value_or(std::forward(default_value)); } constexpr T* operator->() { return &*mOpt; } constexpr const T* operator->() const { return &*mOpt; } constexpr T& operator*() { return *mOpt; } constexpr const T& operator*() const { return *mOpt; } constexpr bool hasValue() const { return mOpt.has_value(); } explicit constexpr operator bool() const { return hasValue(); } constexpr Nullable& operator=(decltype(nullptr)) { reset(); return *this; } constexpr Nullable& operator=(T value) { mOpt = std::move(value); return *this; } constexpr void reset() { mOpt.reset(); } constexpr void swap(Nullable& other) { mOpt.swap(other.mOpt); } private: std::optional mOpt; }; template class Nullable final { public: using value_type = T; constexpr Nullable() : mValue(nullptr) {} explicit constexpr Nullable(decltype(nullptr)) : mValue(nullptr) {} explicit constexpr Nullable(T value) : mValue(std::move(value)) {} constexpr Nullable(const Nullable& other) = default; constexpr Nullable(Nullable&& other) : mValue(std::move(other.value_)) {} ~Nullable() = default; constexpr T& value() & { if (hasValue()) { return mValue; } else { __builtin_abort(); } } constexpr const T& value() const& { if (hasValue()) { return mValue; } else { __builtin_abort(); } } constexpr T&& value() && { if (hasValue()) { return std::move(mValue); } else { __builtin_abort(); } } constexpr const T&& value() const&& { if (hasValue()) { return std::move(mValue); } else { __builtin_abort(); } } template constexpr T valueOr(U&& default_value) const { return hasValue() ? mValue : static_cast(std::forward(default_value)); } constexpr T* operator->() { return &mValue; } constexpr const T* operator->() const { return &mValue; } constexpr T& operator*() { return mValue; } constexpr const T& operator*() const { return mValue; } constexpr bool hasValue() const { return !(mValue == nullptr); } explicit constexpr operator bool() const { return hasValue(); } constexpr Nullable& operator=(const Nullable& other) = default; constexpr Nullable& operator=(Nullable&& other) { mValue = std::move(other.value_); return *this; } constexpr Nullable& operator=(decltype(nullptr)) { reset(); return *this; } constexpr Nullable& operator=(T value) { mValue = std::move(value); return *this; } constexpr void reset() { mValue = nullptr; } constexpr void swap(Nullable& other) { using std::swap; swap(mValue, other.value_); } private: T mValue; }; template void swap(Nullable& a, Nullable& b) { a.swap(b); } template constexpr bool operator==(const Nullable& lhs, decltype(nullptr)) { return !lhs.hasValue(); } template constexpr bool operator!=(const Nullable& lhs, decltype(nullptr)) { return lhs.hasValue(); } template constexpr bool operator==(decltype(nullptr), const Nullable& rhs) { return !rhs.hasValue(); } template constexpr bool operator!=(decltype(nullptr), const Nullable& rhs) { return rhs.hasValue(); } template constexpr bool operator==(const Nullable& lhs, const Nullable& rhs) { return (lhs.hasValue() == rhs.hasValue()) && (!lhs.hasValue() || *lhs == *rhs); } template constexpr bool operator!=(const Nullable& lhs, const Nullable& rhs) { return (lhs.hasValue() != rhs.hasValue()) || (lhs.hasValue() && *lhs != *rhs); } template constexpr bool operator==(const Nullable& lhs, const U& rhs) { return (lhs.hasValue() != isNull(rhs)) && (!lhs.hasValue() || *lhs == rhs); } template constexpr bool operator!=(const Nullable& lhs, const U& rhs) { return (lhs.hasValue() == isNull(rhs)) || (lhs.hasValue() && *lhs != rhs); } template constexpr bool operator==(const T& lhs, const Nullable& rhs) { return (isNull(lhs) != rhs.hasValue()) && (!rhs.hasValue() || lhs == *rhs); } template constexpr bool operator!=(const T& lhs, const Nullable& rhs) { return (isNull(lhs) == rhs.hasValue()) || (rhs.hasValue() && lhs != *rhs); } } // namespace fit } // namespace android::base