1 // Copyright 2018 The Fuchsia Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef LIB_FIT_NULLABLE_H_ 6 #define LIB_FIT_NULLABLE_H_ 7 8 #include <assert.h> 9 10 #include <type_traits> 11 #include <utility> 12 13 #include "optional.h" 14 15 namespace fit { 16 17 // Determines whether a type can be compared with nullptr. 18 template <typename T, typename Comparable = bool> 19 struct is_comparable_with_null : public std::false_type {}; 20 template <typename T> 21 struct is_comparable_with_null<T, decltype(std::declval<const T&>() == nullptr)> 22 : public std::true_type {}; 23 24 // Returns true if a value equals nullptr. 25 template <typename T, typename Comparable = bool> 26 struct is_null_predicate { 27 constexpr bool operator()(const T& value) { return false; } 28 }; 29 template <typename T> 30 struct is_null_predicate<T, decltype(std::declval<const T&>() == nullptr)> { 31 // This test is intended to work for all types that are comparable with 32 // nullptr. Sometimes, the compiler knows that the value can never equal 33 // nullptr and it may complain that the comparison is always false. 34 // For example, this is the case for a function type or a captureless 35 // lambda closure. It's possible to use template selection to match 36 // some of these cases but not all, so just suppress the warning. 37 // The compiler will optimize away the always-false comparison. 38 #pragma GCC diagnostic push 39 #pragma GCC diagnostic ignored "-Waddress" 40 constexpr bool operator()(const T& value) { return value == nullptr; } 41 #pragma GCC diagnostic pop 42 }; 43 template <typename T> 44 constexpr inline bool is_null(const T& value) { 45 return is_null_predicate<T>()(value); 46 } 47 48 // Determines whether a type can be initialized, assigned, and compared 49 // with nullptr. 50 template <typename T> 51 struct is_nullable 52 : public std::integral_constant< 53 bool, 54 std::is_constructible<T, decltype(nullptr)>::value && 55 std::is_assignable<T&, decltype(nullptr)>::value && 56 is_comparable_with_null<T>::value> {}; 57 template <> 58 struct is_nullable<void> : public std::false_type {}; 59 60 // Holds a value or nullptr. 61 // 62 // This class is similar to |std::optional<T>| except that it uses less 63 // storage when the value type can be initialized, assigned, and compared 64 // with nullptr. 65 // 66 // For example: 67 // - sizeof(fit::nullable<void*>) == sizeof(void*) 68 // - sizeof(std::optional<void*>) == sizeof(struct { bool; void*; }) 69 // - sizeof(fit::nullable<int>) == sizeof(struct { bool; int; }) 70 // - sizeof(std::optional<int>) == sizeof(struct { bool; int; }) 71 template <typename T, bool = (is_nullable<T>::value && 72 std::is_constructible<T, T&&>::value && 73 std::is_assignable<T&, T&&>::value)> 74 class nullable final { 75 public: 76 using value_type = T; 77 78 constexpr nullable() = default; 79 explicit constexpr nullable(decltype(nullptr)) {} 80 explicit constexpr nullable(T value) 81 : opt_(std::move(value)) {} 82 nullable(const nullable& other) = default; 83 nullable(nullable&& other) = default; 84 ~nullable() = default; 85 86 constexpr T& value() & { return opt_.value(); } 87 constexpr const T& value() const& { return opt_.value(); } 88 constexpr T&& value() && { return std::move(opt_.value()); } 89 constexpr const T&& value() const&& { return std::move(opt_.value()); } 90 91 template <typename U = T> 92 constexpr T value_or(U&& default_value) const { 93 return opt_.value_or(std::forward<U>(default_value)); 94 } 95 96 constexpr T* operator->() { return &*opt_; } 97 constexpr const T* operator->() const { return &*opt_; } 98 constexpr T& operator*() { return *opt_; } 99 constexpr const T& operator*() const { return *opt_; } 100 101 constexpr bool has_value() const { return opt_.has_value(); } 102 explicit constexpr operator bool() const { return has_value(); } 103 104 nullable& operator=(const nullable& other) = default; 105 nullable& operator=(nullable&& other) = default; 106 107 nullable& operator=(decltype(nullptr)) { 108 reset(); 109 return *this; 110 } 111 112 nullable& operator=(T value) { 113 opt_ = std::move(value); 114 return *this; 115 } 116 117 void reset() { opt_.reset(); } 118 119 void swap(nullable& other) { opt_.swap(other.opt_); } 120 121 private: 122 optional<T> opt_; 123 }; 124 125 template <typename T> 126 class nullable<T, true> final { 127 public: 128 using value_type = T; 129 130 constexpr nullable() 131 : value_(nullptr) {} 132 explicit constexpr nullable(decltype(nullptr)) 133 : value_(nullptr) {} 134 explicit constexpr nullable(T value) 135 : value_(std::move(value)) {} 136 nullable(const nullable& other) = default; 137 nullable(nullable&& other) 138 : value_(std::move(other.value_)) { 139 other.value_ = nullptr; 140 } 141 ~nullable() = default; 142 143 constexpr T& value() & { 144 assert(has_value()); 145 return value_; 146 } 147 constexpr const T& value() const& { 148 assert(has_value()); 149 return value_; 150 } 151 constexpr T&& value() && { 152 assert(has_value()); 153 return std::move(value_); 154 } 155 constexpr const T&& value() const&& { 156 assert(has_value()); 157 return std::move(value_); 158 } 159 160 template <typename U = T> 161 constexpr T value_or(U&& default_value) const { 162 return has_value() ? value_ : static_cast<T>(std::forward<U>(default_value)); 163 } 164 165 constexpr T* operator->() { return &value_; } 166 constexpr const T* operator->() const { return &value_; } 167 constexpr T& operator*() { return value_; } 168 constexpr const T& operator*() const { return value_; } 169 170 constexpr bool has_value() const { return !(value_ == nullptr); } 171 explicit constexpr operator bool() const { return has_value(); } 172 173 nullable& operator=(const nullable& other) = default; 174 nullable& operator=(nullable&& other) { 175 if (&other == this) 176 return *this; 177 value_ = std::move(other.value_); 178 other.value_ = nullptr; 179 return *this; 180 } 181 182 nullable& operator=(decltype(nullptr)) { 183 reset(); 184 return *this; 185 } 186 187 nullable& operator=(T value) { 188 value_ = std::move(value); 189 return *this; 190 } 191 192 void reset() { value_ = nullptr; } 193 194 void swap(nullable& other) { 195 using std::swap; 196 swap(value_, other.value_); 197 } 198 199 private: 200 T value_; 201 }; 202 203 template <typename T> 204 void swap(nullable<T>& a, nullable<T>& b) { 205 a.swap(b); 206 } 207 208 template <typename T> 209 constexpr bool operator==(const nullable<T>& lhs, decltype(nullptr)) { 210 return !lhs.has_value(); 211 } 212 template <typename T> 213 constexpr bool operator!=(const nullable<T>& lhs, decltype(nullptr)) { 214 return lhs.has_value(); 215 } 216 217 template <typename T> 218 constexpr bool operator==(decltype(nullptr), const nullable<T>& rhs) { 219 return !rhs.has_value(); 220 } 221 template <typename T> 222 constexpr bool operator!=(decltype(nullptr), const nullable<T>& rhs) { 223 return rhs.has_value(); 224 } 225 226 template <typename T, typename U> 227 constexpr bool operator==(const nullable<T>& lhs, const nullable<U>& rhs) { 228 return (lhs.has_value() == rhs.has_value()) && (!lhs.has_value() || *lhs == *rhs); 229 } 230 template <typename T, typename U> 231 constexpr bool operator!=(const nullable<T>& lhs, const nullable<U>& rhs) { 232 return (lhs.has_value() != rhs.has_value()) || (lhs.has_value() && *lhs != *rhs); 233 } 234 235 template <typename T, typename U> 236 constexpr bool operator==(const nullable<T>& lhs, const U& rhs) { 237 return (lhs.has_value() != is_null(rhs)) && (!lhs.has_value() || *lhs == rhs); 238 } 239 template <typename T, typename U> 240 constexpr bool operator!=(const nullable<T>& lhs, const U& rhs) { 241 return (lhs.has_value() == is_null(rhs)) || (lhs.has_value() && *lhs != rhs); 242 } 243 244 template <typename T, typename U> 245 constexpr bool operator==(const T& lhs, const nullable<U>& rhs) { 246 return (is_null(lhs) != rhs.has_value()) && (!rhs.has_value() || lhs == *rhs); 247 } 248 template <typename T, typename U> 249 constexpr bool operator!=(const T& lhs, const nullable<U>& rhs) { 250 return (is_null(lhs) == rhs.has_value()) || (rhs.has_value() && lhs != *rhs); 251 } 252 253 } // namespace fit 254 255 #endif // LIB_FIT_NULLABLE_H_ 256