GameLiftPlugin/Source/AWSSDK/Include/aws/crt/Variant.h (489 lines of code) (raw):
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/assert.h>
#include <aws/crt/Utility.h>
#include <algorithm>
#include <type_traits>
#include <utility>
namespace Aws
{
namespace Crt
{
namespace VariantDetail
{
template <typename T> constexpr const T &ConstExprMax(const T &a, const T &b)
{
return (a < b) ? b : a;
}
namespace ParameterPackSize
{
// Returns a max of sizeof(T) over all T in a template parameter pack
template <typename Last> constexpr std::size_t GetMaxSizeOf(std::size_t curMax = 0)
{
return ConstExprMax(curMax, sizeof(Last));
}
template <typename First, typename Second, typename... Rest>
constexpr std::size_t GetMaxSizeOf(std::size_t curMax = 0)
{
return ConstExprMax(curMax, GetMaxSizeOf<Second, Rest...>(ConstExprMax(curMax, sizeof(First))));
}
// some old gcc versions does not work with alignas(Ts..)
template <typename Last> constexpr std::size_t AlignAsPack(std::size_t curMax = 0)
{
return ConstExprMax(curMax, alignof(Last));
}
template <typename First, typename Second, typename... Rest>
constexpr std::size_t AlignAsPack(std::size_t curMax = 0)
{
return ConstExprMax(curMax, AlignAsPack<Second, Rest...>(ConstExprMax(curMax, alignof(First))));
}
} // namespace ParameterPackSize
namespace Index
{
using VariantIndex = short;
template <typename T, typename Last> constexpr VariantIndex GetIndexOf(VariantIndex curIndex = 0)
{
return std::is_same<T, Last>::value ? curIndex : -1;
}
template <typename T, typename First, typename Second, typename... Rest>
constexpr VariantIndex GetIndexOf(VariantIndex curIndex = 0)
{
return std::is_same<T, First>::value ? curIndex : GetIndexOf<T, Second, Rest...>(++curIndex);
}
} // namespace Index
namespace Checker
{
// Returns True if the template parameter pack Ts has a type T, i.e. ContainsType<T, Ts>() == true if T
// is in the list of Ts
template <typename T, typename Last> constexpr bool ContainsType()
{
return std::is_same<T, Last>::value;
}
template <typename T, typename First, typename Second, typename... Rest> constexpr bool ContainsType()
{
return std::is_same<T, First>::value || ContainsType<T, Second, Rest...>();
}
// a case when the template parameter pack is empty (i.e. Variant<>)
template <typename T> constexpr bool ContainsType()
{
return false;
}
template <typename T, typename... Ts> struct HasType
{
static const bool value = ContainsType<T, Ts...>();
};
} // namespace Checker
#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG)
namespace VariantDebug
{
template <typename... Ts> class VariantDebugBrowser
{
public:
VariantDebugBrowser(char *storage) { InitTuple<0, Ts...>(storage); }
std::tuple<typename std::add_pointer<Ts>::type...> as_tuple;
private:
template <IndexT Index, typename First, typename Second, typename... Rest>
void InitTuple(char *storage)
{
First *value = reinterpret_cast<First *>(storage);
std::get<Index>(as_tuple) = value;
InitTuple<Index + 1, Second, Rest...>(storage);
}
template <IndexT Index, typename Last> void InitTuple(char *storage)
{
Last *value = reinterpret_cast<Last *>(storage);
std::get<Index>(as_tuple) = value;
}
};
} // namespace VariantDebug
#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */
} // namespace VariantDetail
template <std::size_t Index, typename... Ts> class VariantAlternative;
/**
* Custom implementation of a Variant type. std::variant requires C++17
* @tparam Ts types of the variant value
*/
template <typename... Ts> class Variant
{
private:
template <std::size_t Index> using ThisVariantAlternative = VariantAlternative<Index, Ts...>;
template <typename OtherT>
using EnableIfOtherIsThisVariantAlternative = typename std::
enable_if<VariantDetail::Checker::HasType<typename std::decay<OtherT>::type, Ts...>::value, int>::type;
public:
using IndexT = VariantDetail::Index::VariantIndex;
static constexpr std::size_t AlternativeCount = sizeof...(Ts);
Variant()
{
using FirstAlternative = typename ThisVariantAlternative<0>::type;
new (m_storage) FirstAlternative();
m_index = 0;
}
Variant(const Variant &other)
{
AWS_FATAL_ASSERT(other.m_index != -1);
m_index = other.m_index;
VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor());
}
Variant(Variant &&other)
{
AWS_FATAL_ASSERT(other.m_index != -1);
m_index = other.m_index;
VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor());
}
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> Variant(const T &val)
{
static_assert(
VariantDetail::Checker::HasType<typename std::decay<T>::type, Ts...>::value,
"This variant does not have such alternative T.");
static_assert(
sizeof(T) <= STORAGE_SIZE,
"Attempting to instantiate a Variant with a type bigger than all alternatives.");
using PlainT = typename std::decay<T>::type;
new (m_storage) PlainT(val);
m_index = VariantDetail::Index::GetIndexOf<PlainT, Ts...>();
AWS_ASSERT(m_index != -1);
}
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> Variant(T &&val)
{
static_assert(
VariantDetail::Checker::HasType<typename std::decay<T>::type, Ts...>::value,
"This variant does not have such alternative T.");
static_assert(
sizeof(T) <= STORAGE_SIZE,
"Attempting to instantiate a Variant with a type bigger than all alternatives.");
using PlainT = typename std::decay<T>::type;
new (m_storage) PlainT(std::forward<T>(val));
m_index = VariantDetail::Index::GetIndexOf<PlainT, Ts...>();
AWS_ASSERT(m_index != -1);
}
// An overload to initialize with an Alternative T in-place
template <typename T, typename... Args> explicit Variant(Aws::Crt::InPlaceTypeT<T>, Args &&...args)
{
static_assert(
VariantDetail::Checker::HasType<typename std::decay<T>::type, Ts...>::value,
"This variant does not have such alternative T.");
static_assert(
sizeof(T) <= STORAGE_SIZE,
"Attempting to instantiate a Variant with a type bigger than all alternatives.");
using PlainT = typename std::decay<T>::type;
new (m_storage) PlainT(std::forward<Args>(args)...);
m_index = VariantDetail::Index::GetIndexOf<PlainT, Ts...>();
AWS_ASSERT(m_index != -1);
}
Variant &operator=(const Variant &other)
{
if (this != &other)
{
AWS_FATAL_ASSERT(other.m_index != -1);
if (m_index != other.m_index)
{
Destroy();
m_index = other.m_index;
VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveConstructor());
}
else
{
VisitorUtil<0, Ts...>::VisitBinary(this, other, CopyMoveAssigner());
}
}
return *this;
}
Variant &operator=(Variant &&other)
{
if (this != &other)
{
AWS_FATAL_ASSERT(other.m_index != -1);
if (m_index != other.m_index)
{
Destroy();
m_index = other.m_index;
VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveConstructor());
}
else
{
VisitorUtil<0, Ts...>::VisitBinary(this, std::move(other), CopyMoveAssigner());
};
}
return *this;
}
/* emplace */
template <typename T, typename... Args, EnableIfOtherIsThisVariantAlternative<T> = 1>
T &emplace(Args &&...args)
{
static_assert(
VariantDetail::Checker::HasType<typename std::decay<T>::type, Ts...>::value,
"This variant does not have such alternative T.");
static_assert(
sizeof(T) <= STORAGE_SIZE,
"Attempting to instantiate a Variant with a type bigger than all alternatives.");
Destroy();
using PlainT = typename std::decay<T>::type;
new (m_storage) PlainT(std::forward<Args>(args)...);
m_index = VariantDetail::Index::GetIndexOf<PlainT, Ts...>();
AWS_ASSERT(m_index != -1);
T *value = reinterpret_cast<T *>(m_storage);
return *value;
}
template <std::size_t Index, typename... Args>
auto emplace(Args &&...args) -> typename ThisVariantAlternative<Index>::type &
{
static_assert(Index < AlternativeCount, "Unknown alternative index to emplace");
using AlternativeT = typename ThisVariantAlternative<Index>::type;
return emplace<AlternativeT, Args...>(std::forward<Args>(args)...);
}
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> bool holds_alternative() const
{
AWS_ASSERT(m_index != -1);
return m_index == VariantDetail::Index::GetIndexOf<T, Ts...>();
}
/* non-const get */
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> T &get()
{
AWS_FATAL_ASSERT(holds_alternative<T>());
T *value = reinterpret_cast<T *>(m_storage);
return *value;
}
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> T *get_if()
{
if (holds_alternative<T>())
{
T *value = reinterpret_cast<T *>(m_storage);
return value;
}
else
{
return nullptr;
}
}
template <std::size_t Index> auto get() -> typename ThisVariantAlternative<Index>::type &
{
static_assert(Index < AlternativeCount, "Unknown alternative index to get");
AWS_FATAL_ASSERT(holds_alternative<Index>());
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *ret = reinterpret_cast<AlternativeT *>(m_storage);
return *ret;
}
/* const get */
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> const T &get() const
{
AWS_FATAL_ASSERT(holds_alternative<T>());
const T *value = reinterpret_cast<const T *>(m_storage);
return *value;
}
template <typename T, EnableIfOtherIsThisVariantAlternative<T> = 1> const T *get_if() const
{
if (holds_alternative<T>())
{
T *value = reinterpret_cast<T *>(m_storage);
return value;
}
else
{
return nullptr;
}
}
template <std::size_t Index> auto get() const -> const typename ThisVariantAlternative<Index>::type &
{
static_assert(Index < AlternativeCount, "Unknown alternative index to get");
AWS_ASSERT(Index == m_index);
using AlternativeT = typename ThisVariantAlternative<Index>::type;
const AlternativeT *ret = reinterpret_cast<const AlternativeT *>(m_storage);
return *ret;
}
/* This is just a templated way to say
* "int*" for
* a VariantAlternative<0, Variant<int, char, long>()>*/
template <std::size_t Index>
using RawAlternativePointerT =
typename std::add_pointer<typename ThisVariantAlternative<Index>::type>::type;
template <std::size_t Index> auto get_if() -> RawAlternativePointerT<Index>
{
static_assert(Index < AlternativeCount, "Unknown alternative index to get");
if (holds_alternative<Index>())
{
using AlternativePtrT = RawAlternativePointerT<Index>;
AlternativePtrT value = reinterpret_cast<AlternativePtrT>(m_storage);
return value;
}
else
{
return nullptr;
}
}
template <std::size_t Index>
using ConstRawAlternativePointerT = typename std::add_pointer<
typename std::add_const<typename ThisVariantAlternative<Index>::type>::type>::type;
template <std::size_t Index> auto get_if() const -> ConstRawAlternativePointerT<Index>
{
static_assert(Index < AlternativeCount, "Unknown alternative index to get");
if (holds_alternative<Index>())
{
using AlternativePtrT = ConstRawAlternativePointerT<Index>;
AlternativePtrT value = reinterpret_cast<AlternativePtrT>(m_storage);
return value;
}
else
{
return nullptr;
}
}
std::size_t index() const { return m_index; }
~Variant() { Destroy(); }
template <typename VisitorT> void Visit(VisitorT &&visitor)
{
return VisitorUtil<0, Ts...>::Visit(this, std::forward<VisitorT>(visitor));
}
private:
static constexpr std::size_t STORAGE_SIZE = VariantDetail::ParameterPackSize::GetMaxSizeOf<Ts...>();
alignas(VariantDetail::ParameterPackSize::AlignAsPack<Ts...>()) char m_storage[STORAGE_SIZE];
IndexT m_index = -1;
#if defined(AWS_CRT_ENABLE_VARIANT_DEBUG)
VariantDetail::VariantDebug::VariantDebugBrowser<Ts...> browser = m_storage;
#endif /* defined(AWS_CRT_ENABLE_VARIANT_DEBUG) */
template <size_t Index> constexpr bool holds_alternative() const { return Index == m_index; }
struct Destroyer
{
template <typename AlternativeT> void operator()(AlternativeT &&value) const
{
using PlaintT = typename std::remove_reference<AlternativeT>::type;
value.~PlaintT();
}
};
void Destroy()
{
AWS_FATAL_ASSERT(m_index != -1);
Visit(Destroyer());
m_index = -1;
}
struct CopyMoveConstructor
{
template <typename AlternativeT> void operator()(AlternativeT &&value, AlternativeT &&other) const
{
using PlaintT = typename std::remove_reference<AlternativeT>::type;
new (&value) PlaintT(std::move<AlternativeT>(other));
}
template <typename AlternativeT, typename ConstAlternativeT>
void operator()(AlternativeT &&value, ConstAlternativeT &other) const
{
using PlaintT = typename std::remove_reference<AlternativeT>::type;
using PlaintOtherT =
typename std::remove_const<typename std::remove_reference<AlternativeT>::type>::type;
static_assert(std::is_same<PlaintT, PlaintOtherT>::value, "Incompatible types");
new (&value) PlaintT(other);
}
};
struct CopyMoveAssigner
{
template <typename AlternativeT> void operator()(AlternativeT &&value, AlternativeT &&other) const
{
value = std::move(other);
}
template <typename AlternativeT, typename ConstAlternativeT>
void operator()(AlternativeT &&value, ConstAlternativeT &other) const
{
using PlaintT = typename std::remove_reference<AlternativeT>::type;
using PlaintOtherT =
typename std::remove_const<typename std::remove_reference<AlternativeT>::type>::type;
static_assert(std::is_same<PlaintT, PlaintOtherT>::value, "Incompatible types");
value = other;
}
};
template <IndexT Index, typename... Args> struct VisitorUtil;
template <IndexT Index, typename First, typename Second, typename... Rest>
struct VisitorUtil<Index, First, Second, Rest...>
{
template <typename VisitorStruct> static void Visit(Variant *pThis, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
visitor(*value);
}
else
{
VisitorUtil<static_cast<IndexT>(Index + 1), Second, Rest...>::Visit(
pThis, std::forward<VisitorStruct>(visitor));
}
}
template <typename VisitorStruct>
static void VisitBinary(Variant<Ts...> *pThis, Variant<Ts...> &&other, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
visitor(*value, other.get<AlternativeT>());
}
else
{
VisitorUtil<static_cast<IndexT>(Index + 1), Second, Rest...>::VisitBinary(
pThis, std::forward<Variant<Ts...>>(other), std::forward<VisitorStruct>(visitor));
}
}
template <typename VisitorStruct>
static void VisitBinary(Variant<Ts...> *pThis, const Variant<Ts...> &other, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
const AlternativeT &otherValue = other.get<AlternativeT>();
visitor(*value, otherValue);
}
else
{
VisitorUtil<static_cast<IndexT>(Index + 1), Second, Rest...>::VisitBinary(
pThis, other, std::forward<VisitorStruct>(visitor));
}
}
};
template <IndexT Index, typename Last> struct VisitorUtil<Index, Last>
{
template <typename VisitorStruct> static void Visit(Variant *pThis, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
visitor(*value);
}
else
{
AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
}
}
template <typename VisitorStruct>
static void VisitBinary(Variant<Ts...> *pThis, Variant<Ts...> &&other, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
visitor(*value, other.get<AlternativeT>());
}
else
{
AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
}
}
template <typename VisitorStruct>
static void VisitBinary(Variant<Ts...> *pThis, const Variant<Ts...> &other, VisitorStruct &&visitor)
{
static_assert(Index < AlternativeCount, "Attempting to visit unknown Index Type");
if (Index == pThis->m_index)
{
using AlternativeT = typename ThisVariantAlternative<Index>::type;
AlternativeT *value = reinterpret_cast<AlternativeT *>(pThis->m_storage);
const AlternativeT &otherValue = other.get<AlternativeT>();
visitor(*value, otherValue);
}
else
{
AWS_FATAL_ASSERT(!"Unknown variant alternative to visit!");
}
}
};
};
/* Helper template to get an actual type from an Index */
template <std::size_t Index, typename... Ts> class VariantAlternative
{
public:
// uses std::tuple as a helper struct to provide index-based access of a parameter pack
using type = typename std::tuple_element<Index, std::tuple<Ts...>>::type;
VariantAlternative(const Variant<Ts...> &) {}
VariantAlternative(const Variant<Ts...> *) {}
};
template <typename T> class VariantSize
{
constexpr static const std::size_t Value = T::AlternativeCount;
};
} // namespace Crt
} // namespace Aws