Skip to content

Commit

Permalink
Optional: allow holding non-default-constructable-type
Browse files Browse the repository at this point in the history
  • Loading branch information
kghost committed Aug 25, 2021
1 parent b9f6c60 commit 39514f3
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 50 deletions.
72 changes: 26 additions & 46 deletions src/lib/core/Optional.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

#include <assert.h>

#include <lib/support/Variant.h>

namespace chip {

/**
Expand All @@ -35,69 +37,34 @@ template <class T>
class Optional
{
public:
constexpr Optional() : mHasValue(false) {}
explicit Optional(const T & value) : mValue(value), mHasValue(true) {}
constexpr Optional() { mVariant.template Set<None>(); }
explicit Optional(const T & value) { mVariant.template Set<Some>(value); }

constexpr Optional(const Optional & other) = default;
constexpr Optional(Optional && other) = default;

/**
* Assignment operator implementation.
*
* NOTE: Manually implemented instead of =default since other::mValue may not be initialized
* if it has no value.
*/
constexpr Optional & operator=(const Optional & other)
{
if (other.HasValue())
{
SetValue(other.Value());
}
else
{
ClearValue();
}
return *this;
}
constexpr Optional & operator=(const Optional & other) = default;

/** Make the optional contain a specific value */
constexpr void SetValue(const T & value)
{
mValue = value;
mHasValue = true;
}
constexpr void SetValue(const T & value) { mVariant.template Set<Some>(value); }

/** Invalidate the value inside the optional. Optional now has no value */
constexpr void ClearValue() { mHasValue = false; }
constexpr void ClearValue() { mVariant.template Set<None>(); }

/** Gets the current value of the optional. Valid IFF `HasValue`. */
const T & Value() const
{
assert(HasValue());
return mValue;
return mVariant.template Get<Some>().mValue;
}

/** 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
{
if (HasValue())
{
return mValue;
}
return defaultValue;
}
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; }
constexpr bool HasValue() const { return !mVariant.template Is<None>(); }

/** Comparison operator, handling missing values. */
bool operator==(const Optional & other) const
{
return (mHasValue == other.mHasValue) && (!other.mHasValue || (mValue == other.mValue));
}

/** Comparison operator, handling missing values. */
bool operator==(const Optional & other) const { return mVariant == other.mVariant; }
bool operator!=(const Optional & other) const { return !(*this == other); }

/** Convenience method to create an optional without a valid value. */
Expand All @@ -107,8 +74,21 @@ class Optional
static Optional<T> Value(const T & value) { return Optional(value); }

private:
T mValue; ///< Value IFF optional contains a value
bool mHasValue; ///< True IFF optional contains a value
struct None
{
static constexpr const std::size_t VariantId = 1;
bool operator==(const None &) const { return true; }
};

struct Some
{
static constexpr const std::size_t VariantId = 2;
Some(const T & value) : mValue(value) {}
bool operator==(const Some & other) const { return mValue == other.mValue; }
T mValue;
};

Variant<None, Some> mVariant;
};

} // namespace chip
25 changes: 21 additions & 4 deletions src/lib/support/Variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,14 @@
#include <core/CHIPCore.h>

#include <algorithm>
#include <cassert>
#include <new>
#include <type_traits>
#include <typeinfo>
#include <utility>

namespace chip {

namespace Internal {
namespace VariantInternal {

template <typename... Ts>
struct VariantCurry;
Expand Down Expand Up @@ -60,6 +59,14 @@ struct VariantCurry<T, Ts...>
else
VariantCurry<Ts...>::Copy(that_t, that_v, this_v);
}

inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
{
if (type_t == T::VariantId)
return *reinterpret_cast<const T *>(this_v) == *reinterpret_cast<const T *>(that_v);
else
return VariantCurry<Ts...>::Equal(type_t, that_v, this_v);
}
};

template <>
Expand All @@ -68,9 +75,14 @@ struct VariantCurry<>
inline static void Destroy(std::size_t id, void * data) {}
inline static void Move(std::size_t that_t, void * that_v, void * this_v) {}
inline static void Copy(std::size_t that_t, const void * that_v, void * this_v) {}
inline static bool Equal(std::size_t type_t, const void * that_v, const void * this_v)
{
VerifyOrDie(false);
return false;
}
};

} // namespace Internal
} // namespace VariantInternal

/**
* @brief
Expand Down Expand Up @@ -101,7 +113,7 @@ struct Variant
static constexpr std::size_t kInvalidType = SIZE_MAX;

using Data = typename std::aligned_storage<kDataSize, kDataAlign>::type;
using Curry = Internal::VariantCurry<Ts...>;
using Curry = VariantInternal::VariantCurry<Ts...>;

std::size_t mTypeId;
Data mData;
Expand Down Expand Up @@ -136,6 +148,11 @@ struct Variant
return *this;
}

bool operator==(const Variant & other) const
{
return GetType() == other.GetType() && Curry::Equal(mTypeId, &other.mData, &mData);
}

template <typename T>
bool Is() const
{
Expand Down

0 comments on commit 39514f3

Please sign in to comment.