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