| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file defines the chip::Optional class to handle values which may |
| * or may not be present. |
| * |
| */ |
| #pragma once |
| |
| #include <new> |
| |
| #include <lib/core/CHIPCore.h> |
| #include <lib/core/InPlace.h> |
| |
| namespace chip { |
| |
| /// An empty class type used to indicate optional type with uninitialized state. |
| struct NullOptionalType |
| { |
| explicit NullOptionalType() = default; |
| }; |
| constexpr NullOptionalType NullOptional{}; |
| |
| /** |
| * Pairs an object with a boolean value to determine if the object value |
| * is actually valid or not. |
| */ |
| template <class T> |
| class Optional |
| { |
| public: |
| constexpr Optional() : mHasValue(false) {} |
| constexpr Optional(NullOptionalType) : mHasValue(false) {} |
| |
| ~Optional() |
| { |
| // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch): mData is set when mHasValue |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| } |
| |
| explicit Optional(const T & value) : mHasValue(true) { new (&mValue.mData) T(value); } |
| |
| template <class... Args> |
| constexpr explicit Optional(InPlaceType, Args &&... args) : mHasValue(true) |
| { |
| new (&mValue.mData) T(std::forward<Args>(args)...); |
| } |
| |
| constexpr Optional(const Optional & other) : mHasValue(other.mHasValue) |
| { |
| if (mHasValue) |
| { |
| new (&mValue.mData) T(other.mValue.mData); |
| } |
| } |
| |
| constexpr Optional(Optional && other) : mHasValue(other.mHasValue) |
| { |
| if (mHasValue) |
| { |
| new (&mValue.mData) T(std::move(other.mValue.mData)); |
| other.mValue.mData.~T(); |
| other.mHasValue = false; |
| } |
| } |
| |
| constexpr Optional & operator=(const Optional & other) |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = other.mHasValue; |
| if (mHasValue) |
| { |
| new (&mValue.mData) T(other.mValue.mData); |
| } |
| return *this; |
| } |
| |
| constexpr Optional & operator=(Optional && other) |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = other.mHasValue; |
| if (mHasValue) |
| { |
| new (&mValue.mData) T(std::move(other.mValue.mData)); |
| other.mValue.mData.~T(); |
| other.mHasValue = false; |
| } |
| return *this; |
| } |
| |
| /// Constructs the contained value in-place |
| template <class... Args> |
| constexpr T & Emplace(Args &&... args) |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = true; |
| new (&mValue.mData) T(std::forward<Args>(args)...); |
| return mValue.mData; |
| } |
| |
| /** Make the optional contain a specific value */ |
| constexpr void SetValue(const T & value) |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = true; |
| new (&mValue.mData) T(value); |
| } |
| |
| /** Make the optional contain a specific value */ |
| constexpr void SetValue(T && value) |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = true; |
| new (&mValue.mData) T(std::move(value)); |
| } |
| |
| /** Invalidate the value inside the optional. Optional now has no value */ |
| constexpr void ClearValue() |
| { |
| if (mHasValue) |
| { |
| mValue.mData.~T(); |
| } |
| mHasValue = false; |
| } |
| |
| /** Gets the current value of the optional. Valid IFF `HasValue`. */ |
| T & Value() & |
| { |
| VerifyOrDie(HasValue()); |
| return mValue.mData; |
| } |
| |
| /** Gets the current value of the optional. Valid IFF `HasValue`. */ |
| const T & Value() const & |
| { |
| VerifyOrDie(HasValue()); |
| return mValue.mData; |
| } |
| |
| /** Gets the current value of the optional if the optional has a value; |
| otherwise returns the provided default value. */ |
| const T & ValueOr(const T & defaultValue) const { return HasValue() ? Value() : defaultValue; } |
| |
| /** Checks if the optional contains a value or not */ |
| constexpr bool HasValue() const { return mHasValue; } |
| |
| bool operator==(const Optional & other) const |
| { |
| return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue.mData == other.mValue.mData)); |
| } |
| bool operator!=(const Optional & other) const { return !(*this == other); } |
| |
| /** Convenience method to create an optional without a valid value. */ |
| static Optional<T> Missing() { return Optional<T>(); } |
| |
| /** Convenience method to create an optional containing the specified value. */ |
| template <class... Args> |
| static Optional<T> Value(Args &&... args) |
| { |
| return Optional(InPlace, std::forward<Args>(args)...); |
| } |
| |
| private: |
| bool mHasValue; |
| union Value |
| { |
| Value() {} |
| ~Value() {} |
| T mData; |
| } mValue; |
| }; |
| |
| template <class T> |
| constexpr Optional<std::decay_t<T>> MakeOptional(T && value) |
| { |
| return Optional<std::decay_t<T>>(InPlace, std::forward<T>(value)); |
| } |
| |
| template <class T, class... Args> |
| constexpr Optional<T> MakeOptional(Args &&... args) |
| { |
| return Optional<T>(InPlace, std::forward<Args>(args)...); |
| } |
| |
| } // namespace chip |