blob: 316473892a3938d57bb228c34ec09b4cf63e59f6 [file] [log] [blame]
/*
*
* 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