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 : template <class T>
48 : class Optional
49 : {
50 : public:
51 786545 : constexpr Optional() {}
52 35426 : constexpr Optional(NullOptionalType) {}
53 :
54 1100 : explicit Optional(const T & value)
55 1100 : {
56 1100 : mValueHolder.mHasValue = true;
57 1100 : new (&mValueHolder.mValue.mData) T(value);
58 1100 : }
59 :
60 : template <class... Args>
61 367590 : constexpr explicit Optional(InPlaceType, Args &&... args)
62 367590 : {
63 367590 : mValueHolder.mHasValue = true;
64 367590 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
65 367590 : }
66 :
67 15277 : constexpr Optional(const Optional & other)
68 15277 : {
69 15277 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
70 15277 : if (mValueHolder.mHasValue)
71 : {
72 3528 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
73 : }
74 15277 : }
75 :
76 : // Converts an Optional of an implicitly convertible type
77 : template <class U, std::enable_if_t<!std::is_same_v<T, U> && std::is_convertible_v<const U, T>, bool> = true>
78 593 : constexpr Optional(const Optional<U> & other)
79 593 : {
80 593 : mValueHolder.mHasValue = other.HasValue();
81 593 : if (mValueHolder.mHasValue)
82 : {
83 590 : new (&mValueHolder.mValue.mData) T(other.Value());
84 : }
85 593 : }
86 :
87 : // Converts an Optional of a type that requires explicit conversion
88 : template <class U,
89 : std::enable_if_t<!std::is_same_v<T, U> && !std::is_convertible_v<const U, T> && std::is_constructible_v<T, const U &>,
90 : bool> = true>
91 1 : constexpr explicit Optional(const Optional<U> & other)
92 1 : {
93 1 : mValueHolder.mHasValue = other.HasValue();
94 1 : if (mValueHolder.mHasValue)
95 : {
96 1 : new (&mValueHolder.mValue.mData) T(other.Value());
97 : }
98 1 : }
99 :
100 135138 : constexpr Optional(Optional && other)
101 135138 : {
102 135138 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
103 135138 : if (mValueHolder.mHasValue)
104 : {
105 134461 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
106 134461 : other.mValueHolder.mValue.mData.~T();
107 134461 : other.mValueHolder.mHasValue = false;
108 : }
109 135138 : }
110 :
111 20990 : constexpr Optional & operator=(const Optional & other)
112 : {
113 20990 : if (mValueHolder.mHasValue)
114 : {
115 2286 : mValueHolder.mValue.mData.~T();
116 : }
117 20990 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
118 20990 : if (mValueHolder.mHasValue)
119 : {
120 8377 : new (&mValueHolder.mValue.mData) T(other.mValueHolder.mValue.mData);
121 : }
122 20990 : return *this;
123 : }
124 :
125 137563 : constexpr Optional & operator=(Optional && other)
126 : {
127 137563 : if (mValueHolder.mHasValue)
128 : {
129 1295 : mValueHolder.mValue.mData.~T();
130 : }
131 137563 : mValueHolder.mHasValue = other.mValueHolder.mHasValue;
132 137563 : if (mValueHolder.mHasValue)
133 : {
134 134893 : new (&mValueHolder.mValue.mData) T(std::move(other.mValueHolder.mValue.mData));
135 134893 : other.mValueHolder.mValue.mData.~T();
136 134893 : other.mValueHolder.mHasValue = false;
137 : }
138 137563 : return *this;
139 : }
140 :
141 : /// Constructs the contained value in-place
142 : template <class... Args>
143 10792 : constexpr T & Emplace(Args &&... args)
144 : {
145 10792 : if (mValueHolder.mHasValue)
146 : {
147 25 : mValueHolder.mValue.mData.~T();
148 : }
149 10792 : mValueHolder.mHasValue = true;
150 10792 : new (&mValueHolder.mValue.mData) T(std::forward<Args>(args)...);
151 10792 : return mValueHolder.mValue.mData;
152 : }
153 :
154 : /** Make the optional contain a specific value */
155 42557 : constexpr void SetValue(const T & value)
156 : {
157 42557 : if (mValueHolder.mHasValue)
158 : {
159 5850 : mValueHolder.mValue.mData.~T();
160 : }
161 42557 : mValueHolder.mHasValue = true;
162 42557 : new (&mValueHolder.mValue.mData) T(value);
163 42557 : }
164 :
165 : constexpr void SetValue(std::optional<T> & value)
166 : {
167 : if (value.has_value())
168 : {
169 : SetValue(*value);
170 : }
171 : else
172 : {
173 : ClearValue();
174 : }
175 : }
176 :
177 : /** Make the optional contain a specific value */
178 5720 : constexpr void SetValue(T && value)
179 : {
180 5720 : if (mValueHolder.mHasValue)
181 : {
182 1414 : mValueHolder.mValue.mData.~T();
183 : }
184 5720 : mValueHolder.mHasValue = true;
185 5720 : new (&mValueHolder.mValue.mData) T(std::move(value));
186 5720 : }
187 :
188 : /** Invalidate the value inside the optional. Optional now has no value */
189 140602 : constexpr void ClearValue()
190 : {
191 140602 : if (mValueHolder.mHasValue)
192 : {
193 11813 : mValueHolder.mValue.mData.~T();
194 : }
195 140602 : mValueHolder.mHasValue = false;
196 140602 : }
197 :
198 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
199 817960 : T & Value() &
200 : {
201 817960 : VerifyOrDie(HasValue());
202 817960 : return mValueHolder.mValue.mData;
203 : }
204 :
205 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
206 294588 : const T & Value() const &
207 : {
208 294588 : VerifyOrDie(HasValue());
209 294588 : return mValueHolder.mValue.mData;
210 : }
211 :
212 : /** Gets the current value of the optional if the optional has a value;
213 : otherwise returns the provided default value. */
214 13804 : const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; }
215 :
216 : /** Checks if the optional contains a value or not */
217 2273735 : constexpr bool HasValue() const { return mValueHolder.mHasValue; }
218 :
219 892 : bool operator==(const Optional & other) const
220 : {
221 1771 : return (mValueHolder.mHasValue == other.mValueHolder.mHasValue) &&
222 1771 : (!other.mValueHolder.mHasValue || (mValueHolder.mValue.mData == other.mValueHolder.mValue.mData));
223 : }
224 61 : bool operator!=(const Optional & other) const { return !(*this == other); }
225 33 : bool operator==(const T & other) const { return HasValue() && Value() == other; }
226 24 : bool operator!=(const T & other) const { return !(*this == other); }
227 :
228 27 : std::optional<T> std_optional() const
229 : {
230 27 : VerifyOrReturnValue(HasValue(), std::nullopt);
231 11 : return std::make_optional(Value());
232 : }
233 :
234 : /** Convenience method to create an optional without a valid value. */
235 146264 : static Optional<T> Missing() { return Optional<T>(); }
236 :
237 : /** Convenience method to create an optional containing the specified value. */
238 : template <class... Args>
239 99 : static Optional<T> Value(Args &&... args)
240 : {
241 99 : return Optional(InPlace, std::forward<Args>(args)...);
242 : }
243 :
244 : private:
245 : // A container of bool + value (without constructor/destructor) when the underlying
246 : // type has a trivial destructor
247 : class TrivialDestructor
248 : {
249 : public:
250 : bool mHasValue = false;
251 : union Value
252 : {
253 830299 : Value() {}
254 : T mData;
255 : } mValue;
256 : };
257 :
258 : // A container of bool + value that destroys the underlying type when mHasValue is true.
259 : // To be used for non-trivial destructor classes.
260 : class NonTrivialDestructor
261 : {
262 : public:
263 511905 : ~NonTrivialDestructor()
264 : {
265 : // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
266 511905 : if (mHasValue)
267 : {
268 233117 : mValue.mData.~T();
269 : }
270 511905 : }
271 :
272 : bool mHasValue = false;
273 : union Value
274 : {
275 511905 : Value() {}
276 511905 : ~Value() {}
277 : T mData;
278 : } mValue;
279 : };
280 :
281 : class ValueHolder : public std::conditional_t<std::is_trivially_destructible_v<T>, TrivialDestructor, NonTrivialDestructor>
282 : {
283 : };
284 :
285 : ValueHolder mValueHolder;
286 : };
287 :
288 : template <class T>
289 987 : constexpr Optional<std::decay_t<T>> MakeOptional(T && value)
290 : {
291 987 : return Optional<std::decay_t<T>>(InPlace, std::forward<T>(value));
292 : }
293 :
294 : template <class T>
295 0 : constexpr Optional<T> FromStdOptional(const std::optional<T> & value)
296 : {
297 0 : VerifyOrReturnValue(value.has_value(), NullOptional);
298 0 : return MakeOptional(*value);
299 : }
300 :
301 : template <class T, class... Args>
302 366504 : constexpr Optional<T> MakeOptional(Args &&... args)
303 : {
304 366504 : return Optional<T>(InPlace, std::forward<Args>(args)...);
305 : }
306 :
307 : } // namespace chip
|