Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * This file defines the chip::Optional class to handle values which may
21 : * or may not be present.
22 : *
23 : */
24 : #pragma once
25 :
26 : #include <new>
27 : #include <optional>
28 : #include <type_traits>
29 : #include <utility>
30 :
31 : #include <lib/core/InPlace.h>
32 : #include <lib/support/CodeUtils.h>
33 :
34 : namespace chip {
35 :
36 : /// An empty class type used to indicate optional type with uninitialized state.
37 : struct NullOptionalType
38 : {
39 : explicit NullOptionalType() = default;
40 : };
41 : inline constexpr NullOptionalType NullOptional{};
42 :
43 : /**
44 : * Pairs an object with a boolean value to determine if the object value
45 : * is actually valid or not.
46 : *
47 : * @note Despite the `constexpr` keywords throughout the class, this class is not a literal type and thus cannot be used in
48 : * compile-time contexts. This is mainly because of the use of placement new.
49 : */
50 : template <class T>
51 : class Optional
52 : {
53 : public:
54 804643 : constexpr Optional() {}
55 31102 : constexpr Optional(NullOptionalType) {}
56 :
57 904 : explicit Optional(const T & value)
58 904 : {
59 904 : mValueHolder.mHasValue = true;
60 904 : new (&mValueHolder.mValue.mData) T(value);
61 904 : }
62 :
63 : template <class... Args>
64 368061 : constexpr explicit Optional(InPlaceType, Args &&... args)
65 368061 : {
66 368061 : mValueHolder.mHasValue = true;
67 368061 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
68 368061 : }
69 :
70 23239 : constexpr Optional(const Optional & other)
71 23239 : {
72 23239 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
73 23239 : if (mValueHolder.mHasValue)
74 : {
75 3744 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
76 : }
77 23239 : }
78 :
79 : // Converts an Optional of an implicitly convertible type
80 : template <class U, std::enable_if_t<!std::is_same_v<T, U> && std::is_convertible_v<const U, T>, bool> = true>
81 520 : constexpr Optional(const Optional<U> & other)
82 520 : {
83 520 : mValueHolder.mHasValue = other.HasValue();
84 520 : if (mValueHolder.mHasValue)
85 : {
86 517 : new (&mValueHolder.mValue.mData) T(other.Value());
87 : }
88 520 : }
89 :
90 : // Converts an Optional of a type that requires explicit conversion
91 : template <class U,
92 : std::enable_if_t<!std::is_same_v<T, U> && !std::is_convertible_v<const U, T> && std::is_constructible_v<T, const U &>,
93 : bool> = true>
94 1 : constexpr explicit Optional(const Optional<U> & other)
95 1 : {
96 1 : mValueHolder.mHasValue = other.HasValue();
97 1 : if (mValueHolder.mHasValue)
98 : {
99 1 : new (&mValueHolder.mValue.mData) T(other.Value());
100 : }
101 1 : }
102 :
103 136158 : constexpr Optional(Optional && other)
104 136158 : {
105 136158 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
106 136158 : if (mValueHolder.mHasValue)
107 : {
108 134691 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
109 134691 : other.mValueHolder.mValue.mData.~T();
110 134691 : other.mValueHolder.mHasValue = false;
111 : }
112 136158 : }
113 :
114 15922 : constexpr Optional & operator=(const Optional & other)
115 : {
116 15922 : if (mValueHolder.mHasValue)
117 : {
118 2091 : mValueHolder.mValue.mData.~T();
119 : }
120 15922 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
121 15922 : if (mValueHolder.mHasValue)
122 : {
123 7823 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
124 : }
125 15922 : return *this;
126 : }
127 :
128 138168 : constexpr Optional & operator=(Optional && other)
129 : {
130 138168 : if (mValueHolder.mHasValue)
131 : {
132 1397 : mValueHolder.mValue.mData.~T();
133 : }
134 138168 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
135 138168 : if (mValueHolder.mHasValue)
136 : {
137 135065 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
138 135065 : other.mValueHolder.mValue.mData.~T();
139 135065 : other.mValueHolder.mHasValue = false;
140 : }
141 138168 : return *this;
142 : }
143 :
144 : /// Constructs the contained value in-place
145 : template <class... Args>
146 10965 : constexpr T & Emplace(Args &&... args)
147 : {
148 10965 : if (mValueHolder.mHasValue)
149 : {
150 25 : mValueHolder.mValue.mData.~T();
151 : }
152 10965 : mValueHolder.mHasValue = true;
153 10965 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
154 10965 : return mValueHolder.mValue.mData;
155 : }
156 :
157 : /** Make the optional contain a specific value */
158 42981 : constexpr void SetValue(const T & value)
159 : {
160 42981 : if (mValueHolder.mHasValue)
161 : {
162 5985 : mValueHolder.mValue.mData.~T();
163 : }
164 42981 : mValueHolder.mHasValue = true;
165 42981 : new (&mValueHolder.mValue.mData) T(value);
166 42981 : }
167 :
168 : constexpr void SetValue(std::optional<T> & value)
169 : {
170 : if (value.has_value())
171 : {
172 : SetValue(*value);
173 : }
174 : else
175 : {
176 : ClearValue();
177 : }
178 : }
179 :
180 : /** Make the optional contain a specific value */
181 5923 : constexpr void SetValue(T && value)
182 : {
183 5923 : if (mValueHolder.mHasValue)
184 : {
185 1379 : mValueHolder.mValue.mData.~T();
186 : }
187 5923 : mValueHolder.mHasValue = true;
188 5923 : new (&mValueHolder.mValue.mData) T(std::move(value));
189 5923 : }
190 :
191 : /** Invalidate the value inside the optional. Optional now has no value */
192 140469 : constexpr void ClearValue()
193 : {
194 140469 : if (mValueHolder.mHasValue)
195 : {
196 11984 : mValueHolder.mValue.mData.~T();
197 : }
198 140469 : mValueHolder.mHasValue = false;
199 140469 : }
200 :
201 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
202 819058 : T & Value() &
203 : {
204 819058 : VerifyOrDie(HasValue());
205 819058 : return mValueHolder.mValue.mData;
206 : }
207 :
208 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
209 300619 : const T & Value() const &
210 : {
211 300619 : VerifyOrDie(HasValue());
212 300619 : return mValueHolder.mValue.mData;
213 : }
214 :
215 : /** Gets the current value of the optional if the optional has a value;
216 : otherwise returns the provided default value. */
217 14010 : const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; }
218 :
219 : /** Checks if the optional contains a value or not */
220 2264963 : constexpr bool HasValue() const { return mValueHolder.mHasValue; }
221 :
222 759 : bool operator==(const Optional & other) const
223 : {
224 1510 : return (mValueHolder.mHasValue == other.mValueHolder.mHasValue) &&
225 1510 : (!other.mValueHolder.mHasValue || (mValueHolder.mValue.mData == other.mValueHolder.mValue.mData));
226 : }
227 55 : bool operator!=(const Optional & other) const { return !(*this == other); }
228 35 : bool operator==(const T & other) const { return HasValue() && Value() == other; }
229 24 : bool operator!=(const T & other) const { return !(*this == other); }
230 :
231 29 : std::optional<T> std_optional() const
232 : {
233 29 : VerifyOrReturnValue(HasValue(), std::nullopt);
234 11 : return std::make_optional(Value());
235 : }
236 :
237 : /** Convenience method to create an optional without a valid value. */
238 146474 : static Optional<T> Missing() { return Optional<T>(); }
239 :
240 : /** Convenience method to create an optional containing the specified value. */
241 : template <class... Args>
242 108 : static Optional<T> Value(Args &&... args)
243 : {
244 108 : return Optional(InPlace, std::forward<Args>(args)...);
245 : }
246 :
247 : private:
248 : // A container of bool + value (without constructor/destructor) when the underlying
249 : // type has a trivial destructor
250 : class TrivialDestructor
251 : {
252 : public:
253 : bool mHasValue = false;
254 : union Value
255 : {
256 852594 : Value() {}
257 : T mData;
258 : } mValue;
259 : };
260 :
261 : // A container of bool + value that destroys the underlying type when mHasValue is true.
262 : // To be used for non-trivial destructor classes.
263 : class NonTrivialDestructor
264 : {
265 : public:
266 512825 : ~NonTrivialDestructor()
267 : {
268 : // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
269 512825 : if (mHasValue)
270 : {
271 233346 : mValue.mData.~T();
272 : }
273 512825 : }
274 :
275 : bool mHasValue = false;
276 : union Value
277 : {
278 512825 : Value() {}
279 512825 : ~Value() {}
280 : T mData;
281 : } mValue;
282 : };
283 :
284 : class ValueHolder : public std::conditional_t<std::is_trivially_destructible_v<T>, TrivialDestructor, NonTrivialDestructor>
285 : {
286 : };
287 :
288 : ValueHolder mValueHolder;
289 : };
290 :
291 : template <class T>
292 1260 : constexpr Optional<std::decay_t<T>> MakeOptional(T && value)
293 : {
294 1260 : return Optional<std::decay_t<T>>(InPlace, std::forward<T>(value));
295 : }
296 :
297 : template <class T>
298 0 : constexpr Optional<T> FromStdOptional(const std::optional<T> & value)
299 : {
300 0 : VerifyOrReturnValue(value.has_value(), NullOptional);
301 0 : return MakeOptional(*value);
302 : }
303 :
304 : template <class T, class... Args>
305 366693 : constexpr Optional<T> MakeOptional(Args &&... args)
306 : {
307 366693 : return Optional<T>(InPlace, std::forward<Args>(args)...);
308 : }
309 :
310 : } // namespace chip
|