Library: generalise into a fully copyable type
After exploring the »nested decorator-chain« implementation variant, I decided to stick to the solution with the λ-vistor, while attempting to level and smooth-out the design. * allow to engage ''any'' «slot» at construction * reverse the order of type parameters to be ascending (as in `std::tuple`) * make it fully copyable, movable, assignable and provide a `swap()`, relying on a variant of the "copy-and-swap"-idiom * add an ''cross-constructor'' for an extended branch set
This commit is contained in:
parent
3e743ff3b5
commit
8c046ee2ea
3 changed files with 312 additions and 396 deletions
|
|
@ -67,92 +67,141 @@ namespace util {
|
|||
,_MaxBufSiz<TYPES...>::siz);
|
||||
};
|
||||
|
||||
/**
|
||||
* Data storage base for sum type with selector
|
||||
*/
|
||||
template<size_t SIZ>
|
||||
struct OpaqueSumType
|
||||
template<typename...TYPES>
|
||||
class BranchCase
|
||||
{
|
||||
size_t case_{0};
|
||||
public:
|
||||
static constexpr auto TOP = sizeof...(TYPES) -1;
|
||||
static constexpr auto SIZ = _MaxBufSiz<TYPES...>::siz;
|
||||
|
||||
template<size_t idx>
|
||||
using SlotType = std::tuple_element_t<idx, tuple<TYPES...>>;
|
||||
|
||||
protected:
|
||||
size_t branch_{0};
|
||||
|
||||
alignas(int64_t)
|
||||
std::byte buffer_[SIZ];
|
||||
|
||||
template<size_t slot, typename T, typename...INITS>
|
||||
T&
|
||||
template<typename TX, typename...INITS>
|
||||
TX&
|
||||
emplace (INITS&&...inits)
|
||||
{
|
||||
case_ = slot;
|
||||
return * new(&buffer_) T(forward<INITS> (inits)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T, typename...TYPES>
|
||||
class SumType
|
||||
: private OpaqueSumType<_MaxBufSiz<T,TYPES...>::siz>
|
||||
{
|
||||
public:
|
||||
static constexpr size_t TOP = sizeof...(TYPES);
|
||||
static constexpr size_t SIZ = _MaxBufSiz<T,TYPES...>::siz;
|
||||
using _Opaque = OpaqueSumType<SIZ>;
|
||||
|
||||
template<typename...INITS, typename = lib::meta::disable_if_self<SumType,INITS...>>
|
||||
SumType (INITS&& ...inits)
|
||||
{
|
||||
_Opaque::template emplace<TOP,T> (forward<INITS> (inits)...);
|
||||
return * new(&buffer_) TX(forward<INITS> (inits)...);
|
||||
}
|
||||
|
||||
template<typename TX>
|
||||
TX&
|
||||
access ()
|
||||
{
|
||||
return * std::launder (reinterpret_cast<TX*> (& _Opaque::buffer_[0]));
|
||||
return * std::launder (reinterpret_cast<TX*> (&buffer_[0]));
|
||||
}
|
||||
|
||||
/** apply generic functor to the currently selected branch */
|
||||
template<size_t idx, class FUN>
|
||||
auto
|
||||
selectBranch (FUN&& fun)
|
||||
{
|
||||
if constexpr (0 < idx)
|
||||
if (branch_ < idx)
|
||||
return selectBranch<idx-1> (forward<FUN>(fun));
|
||||
return fun (get<idx>());
|
||||
}
|
||||
|
||||
BranchCase() = default;
|
||||
public:
|
||||
template<class FUN>
|
||||
auto
|
||||
apply (FUN&& fun)
|
||||
{
|
||||
return selectBranch<TOP> (forward<FUN> (fun));
|
||||
}
|
||||
|
||||
~BranchCase()
|
||||
{
|
||||
apply ([this](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
access<Elm>().~Elm();
|
||||
});
|
||||
}
|
||||
|
||||
template<typename...INITS>
|
||||
BranchCase (size_t idx, INITS&& ...inits)
|
||||
{
|
||||
branch_ = idx;
|
||||
apply ([&,this](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
emplace<Elm> (forward<INITS> (inits)...);
|
||||
});
|
||||
}
|
||||
|
||||
BranchCase (BranchCase const& o)
|
||||
{
|
||||
branch_ = o.branch_;
|
||||
BranchCase& unConst = const_cast<BranchCase&> (o);
|
||||
unConst.apply ([this](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
this->emplace<Elm> (it);
|
||||
});
|
||||
}
|
||||
|
||||
BranchCase (BranchCase && ro)
|
||||
{
|
||||
branch_ = ro.branch_;
|
||||
ro.apply ([this](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
this->emplace<Elm> (move (it));
|
||||
});
|
||||
}
|
||||
|
||||
friend void
|
||||
swap (BranchCase& o1, BranchCase o2)
|
||||
{
|
||||
using std::swap;
|
||||
BranchCase tmp;
|
||||
o1.apply ([&](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
tmp.emplace<Elm> (move (o1.access<Elm>()));
|
||||
});
|
||||
swap (o1.branch_,o2.branch_);
|
||||
o1.apply ([&](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
o1.emplace<Elm> (move (o2.access<Elm>()));
|
||||
});
|
||||
o2.apply ([&](auto& it)
|
||||
{ using Elm = decay_t<decltype(it)>;
|
||||
o2.emplace<Elm> (move (tmp.access<Elm>()));
|
||||
});
|
||||
}
|
||||
|
||||
BranchCase&
|
||||
operator= (BranchCase ref)
|
||||
{
|
||||
swap (*this, ref);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename...MORE>
|
||||
auto
|
||||
moveExtended()
|
||||
{
|
||||
using Extended = BranchCase<TYPES...,MORE...>;
|
||||
Extended& upFaked = reinterpret_cast<Extended&> (*this);
|
||||
return Extended (move (upFaked));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
selected() const
|
||||
{
|
||||
return _Opaque::case_;
|
||||
return branch_;
|
||||
}
|
||||
|
||||
template<size_t slot>
|
||||
using SlotType = std::tuple_element_t<TOP-slot, tuple<T,TYPES...>>;
|
||||
|
||||
template<size_t slot>
|
||||
SlotType<slot>&
|
||||
template<size_t idx>
|
||||
SlotType<idx>&
|
||||
get()
|
||||
{
|
||||
return access<SlotType<slot>>();
|
||||
}
|
||||
|
||||
template<typename TX, typename...TS, class FUN>
|
||||
void
|
||||
select (size_t slot, FUN&& fun)
|
||||
{
|
||||
REQUIRE (slot <= sizeof...(TS));
|
||||
if constexpr (sizeof...(TS))
|
||||
if (0 < slot)
|
||||
select<TS...> (slot-1, forward<FUN>(fun));
|
||||
fun (access<TX>());
|
||||
}
|
||||
|
||||
template<typename TX>
|
||||
void
|
||||
destroyIt (TX& it)
|
||||
{
|
||||
it.~TX();
|
||||
}
|
||||
|
||||
void
|
||||
destroy (size_t slot)
|
||||
{
|
||||
select<T,TYPES...> (slot, [&](auto& it){ destroyIt(it); });
|
||||
}
|
||||
|
||||
~SumType()
|
||||
{
|
||||
destroy (_Opaque::case_);
|
||||
return access<SlotType<idx>>();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -216,15 +216,33 @@ namespace test {
|
|||
void
|
||||
acceptAlternatives()
|
||||
{
|
||||
using Sum = SumType<ushort,char>;
|
||||
SHOW_EXPR(sizeof(Sum));
|
||||
Sum sumt{42};
|
||||
SHOW_EXPR(sumt.selected());
|
||||
SHOW_EXPR(sumt.SIZ);
|
||||
SHOW_EXPR(sumt.TOP);
|
||||
SHOW_TYPE(Sum::_Opaque)
|
||||
SHOW_EXPR(sumt.get<1>());
|
||||
SHOW_EXPR(sumt.get<0>());
|
||||
using Branch = BranchCase<char,ushort>;
|
||||
SHOW_EXPR(sizeof(Branch));
|
||||
Branch b1{1, 42};
|
||||
SHOW_EXPR(b1.selected());
|
||||
SHOW_EXPR(b1.SIZ);
|
||||
SHOW_EXPR(b1.TOP);
|
||||
SHOW_EXPR(b1.get<1>());
|
||||
SHOW_EXPR(b1.get<0>());
|
||||
Branch b2{0,'x'};
|
||||
SHOW_EXPR(b2.selected());
|
||||
SHOW_EXPR(b2.get<1>());
|
||||
SHOW_EXPR(b2.get<0>());
|
||||
Branch b3{b1};
|
||||
SHOW_EXPR(b3.selected());
|
||||
SHOW_EXPR(b3.get<1>());
|
||||
SHOW_EXPR(b3.get<0>());
|
||||
b3 = b2;
|
||||
SHOW_EXPR(b3.selected());
|
||||
SHOW_EXPR(b3.get<1>());
|
||||
SHOW_EXPR(b3.get<0>());
|
||||
auto bx = b1.moveExtended<string>();
|
||||
SHOW_EXPR(sizeof(bx))
|
||||
SHOW_EXPR(bx.SIZ);
|
||||
SHOW_EXPR(bx.TOP);
|
||||
SHOW_EXPR(bx.selected());
|
||||
SHOW_EXPR(bx.get<1>());
|
||||
SHOW_EXPR(bx.get<0>());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue