1 // Copyright 2021 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Copyright 2018 The Fuchsia Authors. All rights reserved. 16 // Use of this source code is governed by a BSD-style license that can be 17 // found in the LICENSE file. 18 19 #pragma once 20 21 #include <assert.h> 22 23 #include <optional> 24 #include <type_traits> 25 #include <utility> 26 27 namespace gfxstream::guest { 28 namespace fit { 29 30 // Determines whether a type can be compared with nullptr. 31 template <typename T, typename Comparable = bool> 32 struct IsComparableWithNull : public std::false_type {}; 33 template <typename T> 34 struct IsComparableWithNull<T, decltype(std::declval<const T&>() == nullptr)> 35 : public std::true_type {}; 36 37 // Suppress the warning when the compiler can see that a nullable value is 38 // never equal to nullptr. 39 #pragma GCC diagnostic push 40 #pragma GCC diagnostic ignored "-Waddress" 41 template <typename T, std::enable_if_t<IsComparableWithNull<T>::value, bool> = true> 42 constexpr inline bool isNull(T&& value) { 43 return std::forward<T>(value) == nullptr; 44 } 45 #pragma GCC diagnostic pop 46 47 template <typename T, std::enable_if_t<!IsComparableWithNull<T>::value, bool> = false> 48 constexpr inline bool isNull(T&&) { 49 return false; 50 } 51 52 // Determines whether a type can be initialized, assigned, and compared 53 // with nullptr. 54 template <typename T> 55 struct IsNullable 56 : public std::integral_constant<bool, 57 std::is_constructible<T, decltype(nullptr)>::value && 58 std::is_assignable<T&, decltype(nullptr)>::value && 59 IsComparableWithNull<T>::value> {}; 60 template <> 61 struct IsNullable<void> : public std::false_type {}; 62 63 // Holds a value or nullptr. 64 // 65 // This class is similar to |std::optional<T>| except that it uses less 66 // storage when the value type can be initialized, assigned, and compared 67 // with nullptr. 68 // 69 // For example: 70 // - sizeof(fit::nullable<void*>) == sizeof(void*) 71 // - sizeof(std::optional<void*>) == sizeof(struct { bool; void*; }) 72 // - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; }) 73 // - sizeof(std::optional<int>) == sizeof(struct { bool; int; }) 74 // 75 // TODO(fxbug.dev/42123486): fit::nullable does not precisely mirror 76 // std::optional. This should be corrected to avoid surprises when switching 77 // between the types. 78 template <typename T, 79 bool = (IsNullable<T>::value && std::is_constructible<T, T&&>::value && 80 std::is_assignable<T&, T&&>::value)> 81 class Nullable final { 82 public: 83 using value_type = T; 84 85 ~Nullable() = default; 86 constexpr Nullable() = default; 87 88 explicit constexpr Nullable(decltype(nullptr)) {} 89 explicit constexpr Nullable(T value) : mOpt(std::move(value)) {} 90 91 constexpr Nullable(const Nullable& other) = default; 92 constexpr Nullable& operator=(const Nullable& other) = default; 93 94 constexpr Nullable(Nullable&& other) = default; 95 constexpr Nullable& operator=(Nullable&& other) = default; 96 97 constexpr T& value() & { return mOpt.value(); } 98 constexpr const T& value() const& { return mOpt.value(); } 99 constexpr T&& value() && { return std::move(mOpt.value()); } 100 constexpr const T&& value() const&& { return std::move(mOpt.value()); } 101 102 template <typename U = T> 103 constexpr T valueOr(U&& default_value) const { 104 return mOpt.value_or(std::forward<U>(default_value)); 105 } 106 107 constexpr T* operator->() { return &*mOpt; } 108 constexpr const T* operator->() const { return &*mOpt; } 109 constexpr T& operator*() { return *mOpt; } 110 constexpr const T& operator*() const { return *mOpt; } 111 112 constexpr bool hasValue() const { return mOpt.has_value(); } 113 explicit constexpr operator bool() const { return hasValue(); } 114 115 constexpr Nullable& operator=(decltype(nullptr)) { 116 reset(); 117 return *this; 118 } 119 120 constexpr Nullable& operator=(T value) { 121 mOpt = std::move(value); 122 return *this; 123 } 124 125 constexpr void reset() { mOpt.reset(); } 126 127 constexpr void swap(Nullable& other) { mOpt.swap(other.mOpt); } 128 129 private: 130 std::optional<T> mOpt; 131 }; 132 133 template <typename T> 134 class Nullable<T, true> final { 135 public: 136 using value_type = T; 137 138 constexpr Nullable() : mValue(nullptr) {} 139 explicit constexpr Nullable(decltype(nullptr)) : mValue(nullptr) {} 140 explicit constexpr Nullable(T value) : mValue(std::move(value)) {} 141 constexpr Nullable(const Nullable& other) = default; 142 constexpr Nullable(Nullable&& other) : mValue(std::move(other.value_)) {} 143 ~Nullable() = default; 144 145 constexpr T& value() & { 146 if (hasValue()) { 147 return mValue; 148 } else { 149 __builtin_abort(); 150 } 151 } 152 constexpr const T& value() const& { 153 if (hasValue()) { 154 return mValue; 155 } else { 156 __builtin_abort(); 157 } 158 } 159 constexpr T&& value() && { 160 if (hasValue()) { 161 return std::move(mValue); 162 } else { 163 __builtin_abort(); 164 } 165 } 166 constexpr const T&& value() const&& { 167 if (hasValue()) { 168 return std::move(mValue); 169 } else { 170 __builtin_abort(); 171 } 172 } 173 174 template <typename U = T> 175 constexpr T valueOr(U&& default_value) const { 176 return hasValue() ? mValue : static_cast<T>(std::forward<U>(default_value)); 177 } 178 179 constexpr T* operator->() { return &mValue; } 180 constexpr const T* operator->() const { return &mValue; } 181 constexpr T& operator*() { return mValue; } 182 constexpr const T& operator*() const { return mValue; } 183 184 constexpr bool hasValue() const { return !(mValue == nullptr); } 185 explicit constexpr operator bool() const { return hasValue(); } 186 187 constexpr Nullable& operator=(const Nullable& other) = default; 188 constexpr Nullable& operator=(Nullable&& other) { 189 mValue = std::move(other.value_); 190 return *this; 191 } 192 193 constexpr Nullable& operator=(decltype(nullptr)) { 194 reset(); 195 return *this; 196 } 197 198 constexpr Nullable& operator=(T value) { 199 mValue = std::move(value); 200 return *this; 201 } 202 203 constexpr void reset() { mValue = nullptr; } 204 205 constexpr void swap(Nullable& other) { 206 using std::swap; 207 swap(mValue, other.value_); 208 } 209 210 private: 211 T mValue; 212 }; 213 214 template <typename T> 215 void swap(Nullable<T>& a, Nullable<T>& b) { 216 a.swap(b); 217 } 218 219 template <typename T> 220 constexpr bool operator==(const Nullable<T>& lhs, decltype(nullptr)) { 221 return !lhs.hasValue(); 222 } 223 template <typename T> 224 constexpr bool operator!=(const Nullable<T>& lhs, decltype(nullptr)) { 225 return lhs.hasValue(); 226 } 227 228 template <typename T> 229 constexpr bool operator==(decltype(nullptr), const Nullable<T>& rhs) { 230 return !rhs.hasValue(); 231 } 232 template <typename T> 233 constexpr bool operator!=(decltype(nullptr), const Nullable<T>& rhs) { 234 return rhs.hasValue(); 235 } 236 237 template <typename T, typename U> 238 constexpr bool operator==(const Nullable<T>& lhs, const Nullable<U>& rhs) { 239 return (lhs.hasValue() == rhs.hasValue()) && (!lhs.hasValue() || *lhs == *rhs); 240 } 241 template <typename T, typename U> 242 constexpr bool operator!=(const Nullable<T>& lhs, const Nullable<U>& rhs) { 243 return (lhs.hasValue() != rhs.hasValue()) || (lhs.hasValue() && *lhs != *rhs); 244 } 245 246 template <typename T, typename U> 247 constexpr bool operator==(const Nullable<T>& lhs, const U& rhs) { 248 return (lhs.hasValue() != isNull(rhs)) && (!lhs.hasValue() || *lhs == rhs); 249 } 250 template <typename T, typename U> 251 constexpr bool operator!=(const Nullable<T>& lhs, const U& rhs) { 252 return (lhs.hasValue() == isNull(rhs)) || (lhs.hasValue() && *lhs != rhs); 253 } 254 255 template <typename T, typename U> 256 constexpr bool operator==(const T& lhs, const Nullable<U>& rhs) { 257 return (isNull(lhs) != rhs.hasValue()) && (!rhs.hasValue() || lhs == *rhs); 258 } 259 template <typename T, typename U> 260 constexpr bool operator!=(const T& lhs, const Nullable<U>& rhs) { 261 return (isNull(lhs) == rhs.hasValue()) || (rhs.hasValue() && lhs != *rhs); 262 } 263 264 } // namespace fit 265 } // namespace gfxstream::guest 266