#ifndef VARIANT_HPP_INCLUDED
#define VARIANT_HPP_INCLUDED
#include <typeindex>
#include <type_traits>
#include <memory>
#include "CPPutils.hpp"
namespace utils
{
/** \defgroup Substitution
* Used in order to substitute one type for another
* \{
*
*/
/** \brief Standard tag type
*
* \tparam T the type
*
*/
template<class T>
struct Tag
{
using type=T; /**< The type of this Tag */
};
/** \brief Substitute is used to derive the correct Tag type
*
* \tparam X the type that will be used by default
* \tparam A the type to check X against
* \tparam B the type to use it both X and A are the same
*
*/
template<class X, class A, class B>
struct Substitute : Tag<X> {};
template<class X, class A, class B>
using SubstituteType = typename Substitute<X,A,B>::type;
template<class A, class B>
struct Substitute<A,A,B> : Tag<B> {};
template<class X, class A, class B>
struct Substitute<X&,A,B> : Tag<SubstituteType<X,A,B>&> {};
template<class X, class A, class B>
struct Substitute<X&&,A,B> : Tag<SubstituteType<X,A,B>&&> {};
template<class X, class A, class B>
struct Substitute<X const,A,B> : Tag<SubstituteType<X,A,B>const> {};
template<class X, class A, class B>
struct Substitute<X volatile,A,B> : Tag<SubstituteType<X,A,B>volatile> {};
template<class X, class A, class B>
struct Substitute<X const volatile,A,B> : Tag<SubstituteType<X,A,B>const volatile> {};
template<template<class...>class Z,class...Xs, class A, class B>
struct Substitute<Z<Xs...>,A,B> : Tag<Z<SubstituteType<Xs,A,B>...>> {};
template<template<class,size_t>class Z,class X,size_t n, class A, class B>
struct Substitute<Z<X,n>,A,B> : Tag<Z<SubstituteType<X,A,B>,n>> {};
template<class R,class...Xs, class A, class B>
struct Substitute<R(Xs...),A,B> : Tag<SubstituteType<R,A,B>(SubstituteType<Xs,A,B>...)> {};
/**
*
* \}
*
*/
/** \brief Used as a placeholder when a template type needs to refer to itself
*
*/
struct OwnType {};
/** \brief Wrapper type to allow a Variant type within a class to hold types of that class
*
*/
template<typename T>
class RecursiveWrapper;
/** \brief Variant type that can hold any value, including itself.
* Values are stored on the stack, not the heap, so this should be relatively efficient.
*
* \tparam Ts A list of unique types, in any order, that this variant is capable of holding. Internally, this sets the
* storage and alignment of the internal memory.
*
*/
template<typename... Ts>
class Variant;
/** \brief Internal class used to iterate through a Variants possible types until a match is found, and then execute the
* desired function.
*
* \tparam T0 The type we are going to test to see if the current stored value is of this type
* \tparam Ts The other types not yet checked.
*
*/
template<typename T0, typename... Ts>
struct VariantHelper;
/** \brief Specialization that allows for the substitution of F with T0 if F is the type of the invoking Variant
*
* \tparam T0 The invoking Variant type
* \tparam F The type we're currently checking
* \tparam Ts The other types not yet checked
*
*/
template<typename T0, typename F, typename... Ts>
struct VariantHelper<T0, F, Ts...>
{
inline static std::type_index InvalidType() { return std::type_index(typeid(void)); }
/** \brief Calls the destructor on the type
*
* \param typeIndex The std::type_index of the value we want to destroy
* \param data A void* containing the data that will be destroyed
*
*/
inline static void Destroy(const std::type_index typeIndex, void* data)
{
if(typeIndex == std::type_index(typeid(SubstituteType<F,OwnType,T0>)))
{
reinterpret_cast<SubstituteType<F,OwnType,T0>*>(data)->~SubstituteType<F,OwnType,T0>();
}
else if(typeIndex == InvalidType())
{
}
else
VariantHelper<T0,Ts...>::Destroy(typeIndex, data);
}
/** \brief Moves the data from oldV into newV
*
* \param oldTypeIndex Type of the value being moved
* \param oldV The old data
* \param newV The new data
*
*/
inline static void Move(const std::type_index oldTypeIndex, void* oldV, void* newV)
{
if(oldTypeIndex == std::type_index(typeid(SubstituteType<F,OwnType,T0>)))
new (newV) SubstituteType<F,OwnType,T0>(std::move(*reinterpret_cast<SubstituteType<F,OwnType,T0>*>(oldV)));
else
VariantHelper<T0,Ts...>::Move(oldTypeIndex,oldV,newV);
}
/** \brief Copies the data from oldV into newV
*
* \param oldTypeIndex The type of the value being copied
* \param oldV The old data
* \param newV The new data
*
*/
inline static void Copy(const std::type_index oldTypeIndex, const void* oldV, void* newV)
{
if(oldTypeIndex == std::type_index(typeid(SubstituteType<F,OwnType,T0>)))
new (newV) SubstituteType<F,OwnType,T0>(*reinterpret_cast<const SubstituteType<F,OwnType,T0>*>(oldV));
else
VariantHelper<T0,Ts...>::Copy(oldTypeIndex,oldV,newV);
}
/** \brief Checks if the provided types is one of the types available in the template params
*
* \param typeToCheck The type to check if valid
* \return true when allowed, false otherwise
*
*/
inline static bool TypeIsValid(const std::type_index typeToCheck)
{
if(typeToCheck == std::type_index(typeid(SubstituteType<F,OwnType,T0>)))
return true;
else
return VariantHelper<T0,Ts...>::TypeIsValid(typeToCheck);
}
inline static bool TypeIsValidRecursiveCheck(const std::type_index recursiveWrapperType)
{
if(recursiveWrapperType == std::type_index(typeid(SubstituteType<F,OwnType,T0>)))
return true;
else
return VariantHelper<T0,Ts...>::TypeIsValidRecursiveCheck(recursiveWrapperType);
}
};
/** \brief Catchall specialization to allow for void type
*
* Possibly should throw a runtime_error if we reach these functions to indicate that I'm not very good at C++
*
* \tparam T0 the final type
*
*/
template<typename T0>
struct VariantHelper<T0>
{
inline static void Destroy(const std::type_index typeIndex, void* data) {}
inline static void Move(const std::type_index oldTypeIndex, void* oldV, void* newV) {}
inline static void Copy(const std::type_index oldTypeIndex, const void* oldV, void* newV) {}
inline static bool TypeIsValid(const std::type_index typeToCheck) { return false; }
inline static bool TypeIsValidRecursiveCheck(const std::type_index recursiveWrapperType) { return false; }
};
template<typename... Ts>
class Variant
{
public:
/** \brief Default constructor initiates the Variant with a void state
*
*/
Variant() : m_typeIndex(InvalidType()){}
/** \brief Copy constructor makes a copy of the data from old in this.
*
* \param old The variant to copy
*
*/
Variant(const Variant<Ts...>& old) : m_typeIndex(old.m_typeIndex)
{
HelperType::Copy(old.m_typeIndex, &old.m_data, &m_data);
}
/** \brief Move constructor transfers the data from old into this.
*
* \param old The Variant to move
*
*/
Variant(Variant<Ts...>&& old) : m_typeIndex(old.m_typeIndex)
{
HelperType::Move(old.m_typeIndex, &old.m_data, &m_data);
}
/** \brief Value constructor will initiate the variant with the T value.
*
* \tparam T The type of value; must be one of the Variants templated types.
* \param value The T value to initiate the Variant with.
*
*/
template<typename T>
Variant(const T& value) : m_typeIndex(InvalidType())
{
Set<T>(value);
}
/** \brief Copy and Swap Assignment operator which copies the content old of old into this
*
* \param old The data to assign to this
*
*/
Variant<Ts...>& operator=(Variant<Ts...> old)
{
Variant<Ts...> temp(*this);
m_typeIndex = old.m_typeIndex;
HelperType::Copy(old.m_typeIndex,&old.m_data,&m_data);
old.m_typeIndex = temp.m_typeIndex;
HelperType::Copy(temp.m_typeIndex,&temp.m_data,&old.m_data);
return *this;
}
/** \brief Helper method to check if the Variant is currently holding a value of type T
*
* \tparam T The type we want to check if the Variant holds
* \return true is the types match, false otherwise
*
*/
template<typename T>
bool Is() const
{
return (m_typeIndex == std::type_index(typeid(T)));
}
/** \brief Helper method to check if the Variant is holding a void type
*
* \return true if non-void, false otherwise
*
*/
bool IsValid() const
{
return (m_typeIndex != std::type_index(typeid(InvalidType())));
}
/** \brief Returns the std::type_index currently being held in the Variant
*
* \return The type being held
*
*/
std::type_index GetTypeIndex() const { return m_typeIndex; }
/** \brief Assignment operator of a type
*
* \tparam T The type we're trying to assign. Must be one of the Variants templated types
* \param rhs The value
* \return Reference to this
*
*/
template<typename T>
Variant<Ts...>& operator=(const T& rhs)
{
Set<T>(rhs);
return *this;
}
/** \brief Sets the storage of the Variant to the value provided.
*
* \tparam T The type we're giving the variant
* \tparam Args The types of the constructor arguments for type T
* \param args The arguments to construct a T value
*
*/
template<typename T, typename... Args>
void Set(Args&&... args)
{
if(!HelperType::TypeIsValid(std::type_index(typeid(T)))
&& !HelperType::TypeIsValidRecursiveCheck(std::type_index(typeid(RecursiveWrapper<T>))))
throw std::logic_error("Type not supported in Variant template (" + std::string(typeid(T).name()) + ")");
if(m_typeIndex != InvalidType())
{
HelperType::Destroy(m_typeIndex,&m_data);
m_typeIndex = InvalidType();
}
if(!HelperType::TypeIsValidRecursiveCheck(std::type_index(typeid(RecursiveWrapper<T>))))
{
new (&m_data) T(std::forward<Args>(args)...);
m_typeIndex = std::type_index(typeid(T));
}
else
{
new (&m_data) RecursiveWrapper<T>(std::forward<Args>(args)...);
m_typeIndex = std::type_index(typeid(RecursiveWrapper<T>));
}
}
/** \brief Casts the Variant to type T
*
* \tparam T The type to cast to
* \return A value of type T if the Variant holds a value of T. Otherwise an exception is thrown.
*
* \exception std::bad_cast The Variant does not hold a value of T
*
*/
template<typename T>
operator T() const { return Get<T>(); }
/** \brief Returns a const reference to the internal value
*
* \return A value of type T
*
* \exception std::bad_cast The Variant does not hold a value of T
*
*/
template<typename T>
const T& Get() const
{
if(m_typeIndex == std::type_index(typeid(T)))
return *reinterpret_cast<const T*>(&m_data);
else if(m_typeIndex == std::type_index(typeid(RecursiveWrapper<T>)))
return (*reinterpret_cast<const RecursiveWrapper<T>*>(&m_data)).Get();
else
throw std::bad_cast();
}
/** \brief Returns a reference to the internal value
*
* \tparam T the type you expect the Variant to hold
*
* \return A reference of type T of the internal value
*
* \exception std::bad_cast The Variant does not hold a value of type T
*
*/
template<typename T>
T& Get()
{
if(m_typeIndex == std::type_index(typeid(T)))
return *reinterpret_cast<T*>(&m_data);
else if(m_typeIndex == std::type_index(typeid(RecursiveWrapper<T>)))
return (*reinterpret_cast<RecursiveWrapper<T>*>(&m_data)).Get();
else
throw std::bad_cast();
}
/** \brief Invokes the helper class to invoke the correct destructor on the internal type*
*/
~Variant()
{
HelperType::Destroy(m_typeIndex,&m_data);
}
private:
using DataType = typename std::aligned_union<1,Ts...>::type; /**< The data type that can store all the templated types */
using HelperType = VariantHelper<Variant<Ts...>,Ts...>; /**< Helper type */
/** \brief Provides the std::type_index of a void type
*
*/
static inline std::type_index InvalidType()
{
return std::type_index(typeid(void));
}
std::type_index m_typeIndex; /**< The std::type_index currently being held by the Variant */
DataType m_data; /**< The storage */
};
template<typename T>
class RecursiveWrapper
{
public:
RecursiveWrapper() {}
~RecursiveWrapper() { }
RecursiveWrapper(const T& t) : m_data(std::make_unique<T>(t)) {}
RecursiveWrapper(T&& t) : m_data(std::make_unique<T>(std::move(t))) {}
RecursiveWrapper& operator=(T& rhs)
{
m_data = std::make_unique<T>(rhs);
return *this;
}
RecursiveWrapper(const RecursiveWrapper& r) : m_data(std::make_unique<T>(r.Get())) {}
RecursiveWrapper(RecursiveWrapper&& r) : m_data(std::move(r.m_data)) {}
RecursiveWrapper& operator=(RecursiveWrapper rhs)
{
std::swap(*this,rhs);
return *this;
}
operator T() const { return *m_data; }
T& Get() const { return *m_data; }
private:
std::unique_ptr<T> m_data;
};
}
#endif // VARIANT_HPP_INCLUDED