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 809963 : constexpr Optional() {}
55 32640 : constexpr Optional(NullOptionalType) {}
56 :
57 938 : explicit Optional(const T & value)
58 938 : {
59 938 : mValueHolder.mHasValue = true;
60 938 : new (&mValueHolder.mValue.mData) T(value);
61 938 : }
62 :
63 : template <class... Args>
64 368672 : constexpr explicit Optional(InPlaceType, Args &&... args)
65 368672 : {
66 368672 : mValueHolder.mHasValue = true;
67 368672 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
68 368672 : }
69 :
70 25585 : constexpr Optional(const Optional & other)
71 25585 : {
72 25585 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
73 25585 : if (mValueHolder.mHasValue)
74 : {
75 4439 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
76 : }
77 25585 : }
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 571 : constexpr Optional(const Optional<U> & other)
82 571 : {
83 571 : mValueHolder.mHasValue = other.HasValue();
84 571 : if (mValueHolder.mHasValue)
85 : {
86 568 : new (&mValueHolder.mValue.mData) T(other.Value());
87 : }
88 571 : }
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 136472 : constexpr Optional(Optional && other)
104 136472 : {
105 136472 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
106 136472 : if (mValueHolder.mHasValue)
107 : {
108 134700 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
109 134700 : other.mValueHolder.mValue.mData.~T();
110 134700 : other.mValueHolder.mHasValue = false;
111 : }
112 136472 : }
113 :
114 17264 : constexpr Optional & operator=(const Optional & other)
115 : {
116 17264 : if (mValueHolder.mHasValue)
117 : {
118 2104 : mValueHolder.mValue.mData.~T();
119 : }
120 17264 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
121 17264 : if (mValueHolder.mHasValue)
122 : {
123 8106 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
124 : }
125 17264 : return *this;
126 : }
127 :
128 138884 : constexpr Optional & operator=(Optional && other)
129 : {
130 138884 : if (mValueHolder.mHasValue)
131 : {
132 1463 : mValueHolder.mValue.mData.~T();
133 : }
134 138884 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
135 138884 : if (mValueHolder.mHasValue)
136 : {
137 135282 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
138 135282 : other.mValueHolder.mValue.mData.~T();
139 135282 : other.mValueHolder.mHasValue = false;
140 : }
141 138884 : return *this;
142 : }
143 :
144 : /// Constructs the contained value in-place
145 : template <class... Args>
146 11051 : constexpr T & Emplace(Args &&... args)
147 : {
148 11051 : if (mValueHolder.mHasValue)
149 : {
150 25 : mValueHolder.mValue.mData.~T();
151 : }
152 11051 : mValueHolder.mHasValue = true;
153 11051 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
154 11051 : return mValueHolder.mValue.mData;
155 : }
156 :
157 : /** Make the optional contain a specific value */
158 43206 : constexpr void SetValue(const T & value)
159 : {
160 43206 : if (mValueHolder.mHasValue)
161 : {
162 5996 : mValueHolder.mValue.mData.~T();
163 : }
164 43206 : mValueHolder.mHasValue = true;
165 43206 : new (&mValueHolder.mValue.mData) T(value);
166 43206 : }
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 6104 : constexpr void SetValue(T && value)
182 : {
183 6104 : if (mValueHolder.mHasValue)
184 : {
185 1448 : mValueHolder.mValue.mData.~T();
186 : }
187 6104 : mValueHolder.mHasValue = true;
188 6104 : new (&mValueHolder.mValue.mData) T(std::move(value));
189 6104 : }
190 :
191 : /** Invalidate the value inside the optional. Optional now has no value */
192 141457 : constexpr void ClearValue()
193 : {
194 141457 : if (mValueHolder.mHasValue)
195 : {
196 12083 : mValueHolder.mValue.mData.~T();
197 : }
198 141457 : mValueHolder.mHasValue = false;
199 141457 : }
200 :
201 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
202 820053 : T & Value() &
203 : {
204 820053 : VerifyOrDie(HasValue());
205 820053 : return mValueHolder.mValue.mData;
206 : }
207 :
208 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
209 301236 : const T & Value() const &
210 : {
211 301236 : VerifyOrDie(HasValue());
212 301236 : 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 14069 : const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; }
218 :
219 : /** Checks if the optional contains a value or not */
220 2273986 : constexpr bool HasValue() const { return mValueHolder.mHasValue; }
221 :
222 770 : bool operator==(const Optional & other) const
223 : {
224 1532 : return (mValueHolder.mHasValue == other.mValueHolder.mHasValue) &&
225 1532 : (!other.mValueHolder.mHasValue || (mValueHolder.mValue.mData == other.mValueHolder.mValue.mData));
226 : }
227 55 : bool operator!=(const Optional & other) const { return !(*this == other); }
228 38 : 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 146529 : 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 862061 : 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 513495 : ~NonTrivialDestructor()
267 : {
268 : // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
269 513495 : if (mHasValue)
270 : {
271 233821 : mValue.mData.~T();
272 : }
273 513495 : }
274 :
275 : bool mHasValue = false;
276 : union Value
277 : {
278 513495 : Value() {}
279 513495 : ~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 1489 : constexpr Optional<std::decay_t<T>> MakeOptional(T && value)
293 : {
294 1489 : 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 367075 : constexpr Optional<T> MakeOptional(Args &&... args)
306 : {
307 367075 : return Optional<T>(InPlace, std::forward<Args>(args)...);
308 : }
309 :
310 : } // namespace chip
|