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 101786 : constexpr Optional() : mHasValue(false) {}
52 15859 : constexpr Optional(NullOptionalType) : mHasValue(false) {}
53 :
54 127800 : ~Optional()
55 : {
56 : // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue
57 127800 : if (mHasValue)
58 : {
59 18175 : mValue.mData.~T();
60 : }
61 127800 : }
62 :
63 556 : explicit Optional(const T & value) : mHasValue(true) { new (&mValue.mData) T(value); }
64 :
65 : template <class... Args>
66 287295 : constexpr explicit Optional(InPlaceType, Args &&... args) : mHasValue(true)
67 : {
68 287295 : new (&mValue.mData) T(std::forward<Args>(args)...);
69 287295 : }
70 :
71 6868 : constexpr Optional(const Optional & other) : mHasValue(other.mHasValue)
72 : {
73 6868 : if (mHasValue)
74 : {
75 861 : new (&mValue.mData) T(other.mValue.mData);
76 : }
77 6868 : }
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 : constexpr Optional(const Optional<U> & other) : mHasValue(other.HasValue())
82 : {
83 : if (mHasValue)
84 : {
85 : new (&mValue.mData) T(other.Value());
86 : }
87 : }
88 :
89 : // Converts an Optional of a type that requires explicit conversion
90 : template <class U,
91 : std::enable_if_t<!std::is_same_v<T, U> && !std::is_convertible_v<const U, T> && std::is_constructible_v<T, const U &>,
92 : bool> = true>
93 : constexpr explicit Optional(const Optional<U> & other) : mHasValue(other.HasValue())
94 : {
95 : if (mHasValue)
96 : {
97 : new (&mValue.mData) T(other.Value());
98 : }
99 : }
100 :
101 134708 : constexpr Optional(Optional && other) : mHasValue(other.mHasValue)
102 : {
103 134708 : if (mHasValue)
104 : {
105 134396 : new (&mValue.mData) T(std::move(other.mValue.mData));
106 134396 : other.mValue.mData.~T();
107 134396 : other.mHasValue = false;
108 : }
109 134708 : }
110 :
111 3976 : constexpr Optional & operator=(const Optional & other)
112 : {
113 3976 : if (mHasValue)
114 : {
115 176 : mValue.mData.~T();
116 : }
117 3976 : mHasValue = other.mHasValue;
118 3976 : if (mHasValue)
119 : {
120 3489 : new (&mValue.mData) T(other.mValue.mData);
121 : }
122 3976 : return *this;
123 : }
124 :
125 1244 : constexpr Optional & operator=(Optional && other)
126 : {
127 1244 : if (mHasValue)
128 : {
129 904 : mValue.mData.~T();
130 : }
131 1244 : mHasValue = other.mHasValue;
132 1244 : if (mHasValue)
133 : {
134 342 : new (&mValue.mData) T(std::move(other.mValue.mData));
135 342 : other.mValue.mData.~T();
136 342 : other.mHasValue = false;
137 : }
138 1244 : return *this;
139 : }
140 :
141 : /// Constructs the contained value in-place
142 : template <class... Args>
143 8479 : constexpr T & Emplace(Args &&... args)
144 : {
145 8479 : if (mHasValue)
146 : {
147 23 : mValue.mData.~T();
148 : }
149 8479 : mHasValue = true;
150 8479 : new (&mValue.mData) T(std::forward<Args>(args)...);
151 8479 : return mValue.mData;
152 : }
153 :
154 : /** Make the optional contain a specific value */
155 25951 : constexpr void SetValue(const T & value)
156 : {
157 25951 : if (mHasValue)
158 : {
159 2954 : mValue.mData.~T();
160 : }
161 25951 : mHasValue = true;
162 25951 : new (&mValue.mData) T(value);
163 25951 : }
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 1997 : constexpr void SetValue(T && value)
179 : {
180 1997 : if (mHasValue)
181 : {
182 1228 : mValue.mData.~T();
183 : }
184 1997 : mHasValue = true;
185 1997 : new (&mValue.mData) T(std::move(value));
186 1997 : }
187 :
188 : /** Invalidate the value inside the optional. Optional now has no value */
189 61570 : constexpr void ClearValue()
190 : {
191 61570 : if (mHasValue)
192 : {
193 9213 : mValue.mData.~T();
194 : }
195 61570 : mHasValue = false;
196 61570 : }
197 :
198 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
199 430523 : T & Value() &
200 : {
201 430523 : VerifyOrDie(HasValue());
202 430523 : return mValue.mData;
203 : }
204 :
205 : /** Gets the current value of the optional. Valid IFF `HasValue`. */
206 65561 : const T & Value() const &
207 : {
208 65561 : VerifyOrDie(HasValue());
209 65561 : return 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 2432 : const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; }
215 :
216 : /** Checks if the optional contains a value or not */
217 1009811 : constexpr bool HasValue() const { return mHasValue; }
218 :
219 317 : bool operator==(const Optional & other) const
220 : {
221 317 : return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue.mData == other.mValue.mData));
222 : }
223 : bool operator!=(const Optional & other) const { return !(*this == other); }
224 : bool operator==(const T & other) const { return HasValue() && Value() == other; }
225 : bool operator!=(const T & other) const { return !(*this == other); }
226 :
227 7 : std::optional<T> std_optional() const
228 : {
229 7 : VerifyOrReturnValue(HasValue(), std::nullopt);
230 0 : return std::make_optional(Value());
231 : }
232 :
233 : /** Convenience method to create an optional without a valid value. */
234 12094 : static Optional<T> Missing() { return Optional<T>(); }
235 :
236 : /** Convenience method to create an optional containing the specified value. */
237 : template <class... Args>
238 47 : static Optional<T> Value(Args &&... args)
239 : {
240 47 : return Optional(InPlace, std::forward<Args>(args)...);
241 : }
242 :
243 : private:
244 : bool mHasValue;
245 : union Value
246 : {
247 127119 : Value() {}
248 127800 : ~Value() {}
249 : T mData;
250 : } mValue;
251 : };
252 :
253 : template <class T>
254 954 : constexpr Optional<std::decay_t<T>> MakeOptional(T && value)
255 : {
256 954 : return Optional<std::decay_t<T>>(InPlace, std::forward<T>(value));
257 : }
258 :
259 : template <class T>
260 : constexpr Optional<T> FromStdOptional(const std::optional<T> & value)
261 : {
262 : VerifyOrReturnValue(value.has_value(), NullOptional);
263 : return MakeOptional(*value);
264 : }
265 :
266 : template <class T, class... Args>
267 286294 : constexpr Optional<T> MakeOptional(Args &&... args)
268 : {
269 286294 : return Optional<T>(InPlace, std::forward<Args>(args)...);
270 : }
271 :
272 : } // namespace chip
|