#ifndef LIB_HETERO_DATA_H
#define LIB_HETERO_DATA_H
#include <utility>
#include <tuple>
template<typename...DATA>
class HeteroData;
struct StorageLoc
{
StorageLoc* next{nullptr};
};
template<size_t seg, typename...DATA>
struct StorageFrame
: protected StorageLoc
, std::tuple<DATA...>
{
using Tuple = std::tuple<DATA...>;
using Tuple::tuple;
template<typename SPEC>
void linkInto (HeteroData<SPEC>&);
template<size_t slot> auto& get() noexcept { return std::get<slot>(*this); }
template<typename X> auto& get() noexcept { return std::get<X>(*this); }
};
template<size_t seg, typename...DATA, class TAIL>
class HeteroData<meta::Node<StorageFrame<seg, DATA...>,TAIL>>
: StorageFrame<seg, DATA...>
{
using _Self = HeteroData;
using _Tail = HeteroData<TAIL>;
using Tuple = std::tuple<DATA...>;
using Frame = StorageFrame<seg, DATA...>;
static constexpr size_t localSiz = sizeof...(DATA);
template<size_t slot>
static constexpr
bool isLocal =
slot < localSiz;
template<size_t slot>
using PickType = std::conditional_t<isLocal<slot>, std::tuple_element<slot,Tuple>
,
typename _Tail::template PickType<
slot-localSiz>>;
_Tail&
accessTail()
{
REQUIRE (Frame::next, "HeteroData storage logic broken: follow-up extent not yet allocated");
return * reinterpret_cast<_Tail*> (Frame::next);
}
template<typename...SPEC>
static _Self&
recast (HeteroData<SPEC...>& frontChain)
{
return reinterpret_cast<_Self&> (frontChain);
}
template<typename...SPEC>
static _Self const&
recast (HeteroData<SPEC...> const& frontChain)
{
return reinterpret_cast<_Self const&> (frontChain);
}
template<typename...XX>
friend class HeteroData;
using Frame::Frame;
public:
HeteroData() = default;
static constexpr size_t
size()
{
return localSiz + _Tail::size();
}
template<size_t slot>
using Elm_t = typename PickType<slot>::type;
template<size_t slot>
Elm_t<slot>&
get() noexcept
{
static_assert (
slot < size(),
"HeteroData access index beyond defined data");
if constexpr (
slot < localSiz)
return std::get<slot> (*this);
else
return accessTail().template
get<
slot-localSiz>();
}
template<size_t slot>
Elm_t<slot> const&
get() const noexcept
{
return const_cast<HeteroData*>(this)->get<slot>();
}
template<size_t slot>
struct Accessor
{
using Type = Elm_t<slot>;
template<class SPEC>
static Type&
get (HeteroData<SPEC>& frontEnd)
{
auto& fullChain = _Self::recast (frontEnd);
return fullChain.template get<slot>();
}
template<typename HH>
Type& operator() (HH& frontEnd) const { return Accessor::get(frontEnd); }
};
template<typename...VALS>
struct Chain
{
using Segments = meta::Node<Frame,TAIL>;
using NewFrame = StorageFrame<meta::count<Segments>::value, VALS...>;
using ChainType =
HeteroData<typename meta::Append<Segments,NewFrame>::List>;
template<typename...INIT>
static NewFrame
build (INIT&& ...initArgs)
{
return {initArgs ...};
}
template<class HET>
static auto&
recast (HET& frontChain)
{
return ChainType::recast (frontChain);
}
template<typename...XVALS>
using ChainExtent = typename ChainType::template Chain<XVALS...>;
template<size_t slot>
using Accessor = typename ChainType::template Accessor<_Self::size()+slot>;
template<typename X>
using AccessorFor = Accessor<
meta::indexOfType<X,VALS...>()>;
};
};
template<>
class HeteroData<meta::NullType>
{
public:
static size_t constexpr size() { return 0; }
template<size_t>
using Elm_t = void;
template<size_t>
using PickType = void;
};
template<typename...DATA>
class HeteroData
: public HeteroData<meta::Node<StorageFrame<0, DATA...>, meta::NullType>>
{
using _Front = HeteroData<meta::Node<StorageFrame<0, DATA...>, meta::NullType>>;
public:
using NewFrame = typename _Front::Frame;
using ChainType = _Front;
template<typename...INIT>
static _Front
build (INIT&& ...initArgs)
{
return {initArgs ...};
}
};
namespace {
inline StorageLoc*&
{
REQUIRE(last);
while (segments and last->next)
{
last = last->next;
--segments;
}
ASSERT (last->next == nullptr and segments == 1
,"Failure to attach new data segment to HeteroData: "
"assumed type structure does not match real connectivity, "
"end-of-chain encountered with %d type segment(s) remaining"
, segments);
return last->next;
}
}
template<size_t seg, typename...DATA>
template<typename SPEC>
inline void
{
StorageLoc* firstSeg = reinterpret_cast<StorageLoc*> (&prefixChain);
ENSURE (lastLink == nullptr);
lastLink = this;
}
}
template<typename...DATA>
struct tuple_size<lib::HeteroData<DATA...> >
: std::integral_constant<std::size_t, lib::HeteroData<DATA...>::size()>
{ };
template<size_t I, typename...DATA>
struct tuple_element<I, lib::HeteroData<DATA...> >
{
};
template<size_t I>
struct tuple_element<I, lib::HeteroData<lib::meta::NullType> >
{
static_assert ("accessing element-type of an empty HeteroData block");
};
template<size_t seg, typename...DATA>
struct tuple_size<lib::StorageFrame<seg,DATA...> >
: std::tuple_size<typename lib::StorageFrame<seg,DATA...>::Tuple>
{ };
template<size_t I, size_t seg, typename...DATA>
struct tuple_element<I, lib::StorageFrame<seg,DATA...> >
: std::tuple_element<I, typename lib::StorageFrame<seg,DATA...>::Tuple>
{ };
}
#endif