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 <type_traits> 28 : #include <utility> 29 : 30 : #include <lib/core/InPlace.h> 31 : #include <lib/support/CodeUtils.h> 32 : 33 : namespace chip { 34 : 35 : /// An empty class type used to indicate optional type with uninitialized state. 36 : struct NullOptionalType 37 : { 38 : explicit NullOptionalType() = default; 39 : }; 40 : inline constexpr NullOptionalType NullOptional{}; 41 : 42 : /** 43 : * Pairs an object with a boolean value to determine if the object value 44 : * is actually valid or not. 45 : */ 46 : template <class T> 47 : class Optional 48 : { 49 : public: 50 399798 : constexpr Optional() : mHasValue(false) {} 51 23146 : constexpr Optional(NullOptionalType) : mHasValue(false) {} 52 : 53 604912 : ~Optional() 54 : { 55 : // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue 56 604912 : if (mHasValue) 57 : { 58 193458 : mValue.mData.~T(); 59 : } 60 604912 : } 61 : 62 4356 : explicit Optional(const T & value) : mHasValue(true) { new (&mValue.mData) T(value); } 63 : 64 : template <class... Args> 65 314336 : constexpr explicit Optional(InPlaceType, Args &&... args) : mHasValue(true) 66 : { 67 314336 : new (&mValue.mData) T(std::forward<Args>(args)...); 68 314336 : } 69 : 70 5513 : constexpr Optional(const Optional & other) : mHasValue(other.mHasValue) 71 : { 72 5513 : if (mHasValue) 73 : { 74 2488 : new (&mValue.mData) T(other.mValue.mData); 75 : } 76 5513 : } 77 : 78 : // Converts an Optional of an implicitly convertible type 79 : template <class U, std::enable_if_t<!std::is_same_v<T, U> && std::is_convertible_v<const U, T>, bool> = true> 80 : constexpr Optional(const Optional<U> & other) : mHasValue(other.HasValue()) 81 : { 82 : if (mHasValue) 83 : { 84 : new (&mValue.mData) T(other.Value()); 85 : } 86 : } 87 : 88 : // Converts an Optional of a type that requires explicit conversion 89 : template <class U, 90 : std::enable_if_t<!std::is_same_v<T, U> && !std::is_convertible_v<const U, T> && std::is_constructible_v<T, const U &>, 91 : bool> = true> 92 : constexpr explicit Optional(const Optional<U> & other) : mHasValue(other.HasValue()) 93 : { 94 : if (mHasValue) 95 : { 96 : new (&mValue.mData) T(other.Value()); 97 : } 98 : } 99 : 100 134901 : constexpr Optional(Optional && other) : mHasValue(other.mHasValue) 101 : { 102 134901 : if (mHasValue) 103 : { 104 134476 : new (&mValue.mData) T(std::move(other.mValue.mData)); 105 134476 : other.mValue.mData.~T(); 106 134476 : other.mHasValue = false; 107 : } 108 134901 : } 109 : 110 6874 : constexpr Optional & operator=(const Optional & other) 111 : { 112 6874 : if (mHasValue) 113 : { 114 1833 : mValue.mData.~T(); 115 : } 116 6874 : mHasValue = other.mHasValue; 117 6874 : if (mHasValue) 118 : { 119 3796 : new (&mValue.mData) T(other.mValue.mData); 120 : } 121 6874 : return *this; 122 : } 123 : 124 4379 : constexpr Optional & operator=(Optional && other) 125 : { 126 4379 : if (mHasValue) 127 : { 128 1035 : mValue.mData.~T(); 129 : } 130 4379 : mHasValue = other.mHasValue; 131 4379 : if (mHasValue) 132 : { 133 382 : new (&mValue.mData) T(std::move(other.mValue.mData)); 134 382 : other.mValue.mData.~T(); 135 382 : other.mHasValue = false; 136 : } 137 4379 : return *this; 138 : } 139 : 140 : /// Constructs the contained value in-place 141 : template <class... Args> 142 7913 : constexpr T & Emplace(Args &&... args) 143 : { 144 7913 : if (mHasValue) 145 : { 146 23 : mValue.mData.~T(); 147 : } 148 7913 : mHasValue = true; 149 7913 : new (&mValue.mData) T(std::forward<Args>(args)...); 150 7913 : return mValue.mData; 151 : } 152 : 153 : /** Make the optional contain a specific value */ 154 26063 : constexpr void SetValue(const T & value) 155 : { 156 26063 : if (mHasValue) 157 : { 158 5211 : mValue.mData.~T(); 159 : } 160 26063 : mHasValue = true; 161 26063 : new (&mValue.mData) T(value); 162 26063 : } 163 : 164 : /** Make the optional contain a specific value */ 165 1988 : constexpr void SetValue(T && value) 166 : { 167 1988 : if (mHasValue) 168 : { 169 1228 : mValue.mData.~T(); 170 : } 171 1988 : mHasValue = true; 172 1988 : new (&mValue.mData) T(std::move(value)); 173 1988 : } 174 : 175 : /** Invalidate the value inside the optional. Optional now has no value */ 176 33141 : constexpr void ClearValue() 177 : { 178 33141 : if (mHasValue) 179 : { 180 9050 : mValue.mData.~T(); 181 : } 182 33141 : mHasValue = false; 183 33141 : } 184 : 185 : /** Gets the current value of the optional. Valid IFF `HasValue`. */ 186 472616 : T & Value() & 187 : { 188 472616 : VerifyOrDie(HasValue()); 189 472616 : return mValue.mData; 190 : } 191 : 192 : /** Gets the current value of the optional. Valid IFF `HasValue`. */ 193 112011 : const T & Value() const & 194 : { 195 112011 : VerifyOrDie(HasValue()); 196 112011 : return mValue.mData; 197 : } 198 : 199 : /** Gets the current value of the optional if the optional has a value; 200 : otherwise returns the provided default value. */ 201 1698 : const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; } 202 : 203 : /** Checks if the optional contains a value or not */ 204 1244787 : constexpr bool HasValue() const { return mHasValue; } 205 : 206 295 : bool operator==(const Optional & other) const 207 : { 208 295 : return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue.mData == other.mValue.mData)); 209 : } 210 18 : bool operator!=(const Optional & other) const { return !(*this == other); } 211 : bool operator==(const T & other) const { return HasValue() && Value() == other; } 212 : bool operator!=(const T & other) const { return !(*this == other); } 213 : 214 : /** Convenience method to create an optional without a valid value. */ 215 16598 : static Optional<T> Missing() { return Optional<T>(); } 216 : 217 : /** Convenience method to create an optional containing the specified value. */ 218 : template <class... Args> 219 88 : static Optional<T> Value(Args &&... args) 220 : { 221 88 : return Optional(InPlace, std::forward<Args>(args)...); 222 : } 223 : 224 : private: 225 : bool mHasValue; 226 : union Value 227 : { 228 599836 : Value() {} 229 604912 : ~Value() {} 230 : T mData; 231 : } mValue; 232 : }; 233 : 234 : template <class T> 235 4222 : constexpr Optional<std::decay_t<T>> MakeOptional(T && value) 236 : { 237 4222 : return Optional<std::decay_t<T>>(InPlace, std::forward<T>(value)); 238 : } 239 : 240 : template <class T, class... Args> 241 310022 : constexpr Optional<T> MakeOptional(Args &&... args) 242 : { 243 310022 : return Optional<T>(InPlace, std::forward<Args>(args)...); 244 : } 245 : 246 : } // namespace chip