/* Tuples or OctaSTD. Partially taken from the libc++ project. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OSTD_TUPLE_HH #define OSTD_TUPLE_HH #include "ostd/internal/tuple.hh" #include "ostd/types.hh" #include "ostd/type_traits.hh" #include "ostd/memory.hh" #include "ostd/utility.hh" namespace ostd { /* tuple size */ template constexpr Size TupleSize> = sizeof...(T); /* tuple element */ template struct TupleElementBase> { using Type = typename TupleElementBase>::Type; }; /* tuple leaf */ namespace detail { template> struct TupleLeaf { constexpr TupleLeaf(): p_value() { static_assert(!IsReference, "attempt to default construct a reference element in a tuple"); } template TupleLeaf(Constant, const A &): p_value() { static_assert(!IsReference, "attempt to default construct a reference element in a tuple"); } template TupleLeaf(Constant, const A &a): p_value(allocator_arg, a) { static_assert(!IsReference, "attempt to default construct a reference element in a tuple"); } template TupleLeaf(Constant, const A &a): p_value(a) { static_assert(!IsReference, "attempt to default construct a reference element in a tuple"); } template, TupleLeaf> && IsConstructible >> explicit TupleLeaf(T &&t): p_value(forward(t)) { static_assert(!IsReference || (IsLvalueReference && (IsLvalueReference || IsSame, ReferenceWrapper>>)) || (IsRvalueReference && !IsLvalueReference), "attempt to construct a reference element in a tuple with an rvalue"); } template explicit TupleLeaf(Constant, const A &, T &&t): p_value(forward(t)) { static_assert(!IsLvalueReference || (IsLvalueReference && (IsLvalueReference || IsSame, ReferenceWrapper>>)), "attempt to construct a reference element in a tuple with an rvalue"); } template explicit TupleLeaf(Constant, const A &a, T &&t): p_value(allocator_arg, a, forward(t)) { static_assert(!IsLvalueReference || (IsLvalueReference && (IsLvalueReference || IsSame, ReferenceWrapper>>)), "attempt to construct a reference element in a tuple with an rvalue"); } template explicit TupleLeaf(Constant, const A &a, T &&t): p_value(forward(t), a) { static_assert(!IsLvalueReference || (IsLvalueReference && (IsLvalueReference || IsSame, ReferenceWrapper>>)), "attempt to construct a reference element in a tuple with an rvalue"); } TupleLeaf(const TupleLeaf &) = default; TupleLeaf(TupleLeaf &&) = default; template TupleLeaf &operator=(T &&t) { p_value = forward(t); return *this; } void swap(TupleLeaf &t) { swap_adl(get(), t.get()); } H &get() { return p_value; } const H &get() const { return p_value; } private: TupleLeaf &operator=(const TupleLeaf &); H p_value; }; template struct TupleLeaf: private H { constexpr TupleLeaf() {} template TupleLeaf(Constant, const A &) {} template TupleLeaf(Constant, const A &a): H(allocator_arg, a) {} template TupleLeaf(Constant, const A &a): H(a) {} template, TupleLeaf> && IsConstructible >> explicit TupleLeaf(T &&t): H(forward(t)) {} template explicit TupleLeaf(Constant, const A &, T &&t): H(forward(t)) {} template explicit TupleLeaf(Constant, const A &a, T &&t): H(allocator_arg, a, forward(t)) {} template explicit TupleLeaf(Constant, const A &a, T &&t): H(forward(t), a) {} TupleLeaf(const TupleLeaf &) = default; TupleLeaf(TupleLeaf &&) = default; template TupleLeaf &operator=(T &&t) { H::operator=(forward(t)); return *this; } void swap(TupleLeaf &t) { swap_adl(get(), t.get()); } H &get() { return (H &)*this; } const H &get() const { return (const H &)*this; } private: TupleLeaf &operator=(const TupleLeaf &); }; } /* namespace detail */ /* internal utils */ namespace detail { template inline void tuple_swallow(A &&...) {} template constexpr bool TupleAll = true; template constexpr bool TupleAll = B && TupleAll; template constexpr bool TupleAllDefaultConstructible = detail::Undefined(); template constexpr bool TupleAllDefaultConstructible> = TupleAll...>; } /* tuple implementation */ namespace detail { template struct TupleBase; template struct TupleBase, A...>: TupleLeaf... { constexpr TupleBase() {} template explicit TupleBase(TupleIndices, TupleTypes, TupleIndices, TupleTypes, T &&...t): TupleLeaf(forward(t))..., TupleLeaf()... {} template explicit TupleBase(AllocatorArg, const Alloc &a, TupleIndices, TupleTypes, TupleIndices, TupleTypes, T &&...t): TupleLeaf(UsesAllocatorConstructor, a, forward(t))..., TupleLeaf(UsesAllocatorConstructor, a)... {} template> >> TupleBase(T &&t): TupleLeaf(forward< TupleElement> >(get(t)))... {} template> >> TupleBase(AllocatorArg, const Alloc &a, T &&t): TupleLeaf(UsesAllocatorConstructor< A, Alloc, TupleElement> >, a, forward>>(get(t)))... {} template EnableIf>, TupleBase &> operator=(T &&t) { tuple_swallow(TupleLeaf::operator=(forward< TupleElement> >(get(t)))...); return *this; } TupleBase(const TupleBase &) = default; TupleBase(TupleBase &&) = default; TupleBase &operator=(const TupleBase &t) { tuple_swallow(TupleLeaf::operator=(((const TupleLeaf &)t).get())...); return *this; } TupleBase &operator=(TupleBase &&t) { tuple_swallow(TupleLeaf::operator=(forward (((const TupleLeaf &)t).get()))...); return *this; } void swap(TupleBase &t) { tuple_swallow(TupleLeaf::swap((TupleLeaf &)t)...); } }; } /* namespace detail */ template class Tuple { using Base = detail::TupleBase, A...>; Base p_base; template friend TupleElement> &get(Tuple &); template friend const TupleElement> &get(const Tuple &); template friend TupleElement> &&get(Tuple &&); template friend const TupleElement> &&get(const Tuple &&); public: template)...> >> Tuple() {} explicit Tuple(const A &...t): p_base(detail::MakeTupleIndices(), detail::MakeTupleTypes(), detail::MakeTupleIndices<0>(), detail::MakeTupleTypes(), t...) {} template Tuple(AllocatorArg, const Alloc &a, const A &...t): p_base(allocator_arg, a, detail::MakeTupleIndices(), detail::MakeTupleTypes(), detail::MakeTupleIndices<0>(), detail::MakeTupleTypes(), t...) {} template, detail::MakeTupleTypes > && detail::TupleAllDefaultConstructible< detail::MakeTupleTypes >, bool > = true> Tuple(T &&...t): p_base(detail::MakeTupleIndices(), detail::MakeTupleTypes(), detail::MakeTupleIndices(), detail::MakeTupleTypes(), forward(t)...) {} template, detail::MakeTupleTypes > && !detail::TupleConvertible< Tuple, detail::MakeTupleTypes > && detail::TupleAllDefaultConstructible< detail::MakeTupleTypes >, bool > = true> Tuple(T &&...t): p_base(detail::MakeTupleIndices(), detail::MakeTupleTypes(), detail::MakeTupleIndices(), detail::MakeTupleTypes(), forward(t)...) {} template, detail::MakeTupleTypes > && detail::TupleAllDefaultConstructible< detail::MakeTupleTypes > >> Tuple(AllocatorArg, const Alloc &a, T &&...t): p_base(allocator_arg, a, detail::MakeTupleIndices(), detail::MakeTupleTypes(), detail::MakeTupleIndices(), detail::MakeTupleTypes(), forward(t)...) {} template, bool > = true> Tuple(T &&t): p_base(forward(t)) {} template && !detail::TupleConvertible, bool > = true> Tuple(T &&t): p_base(forward(t)) {} template >> Tuple(AllocatorArg, const Alloc &a, T &&t): p_base(allocator_arg, a, forward(t)) {} template>> Tuple &operator=(T &&t) { p_base.operator=(forward(t)); return *this; } void swap(Tuple &t) { p_base.swap(t.p_base); } }; template<> class Tuple<> { public: constexpr Tuple() {} template Tuple(AllocatorArg, const A &) {} template Tuple(AllocatorArg, const A &, const Tuple &) {} void swap(Tuple &) {} }; /* get */ template inline TupleElement> &get(Tuple &t) { using Type = TupleElement>; return ((detail::TupleLeaf &)t.p_base).get(); } template inline const TupleElement> &get(const Tuple &t) { using Type = TupleElement>; return ((const detail::TupleLeaf &)t.p_base).get(); } template inline TupleElement> &&get(Tuple &&t) { using Type = TupleElement>; return (Type &&)(((detail::TupleLeaf &&)t.p_base).get()); } template inline const TupleElement> &&get(const Tuple &&t) { using Type = TupleElement>; return (const Type &&)(((const detail::TupleLeaf &&)t.p_base).get()); } /* tie */ template inline Tuple tie(T &...t) { return Tuple(t...); } /* ignore */ namespace detail { struct Ignore { template const Ignore &operator=(T &&) const { return *this; } }; } static const detail::Ignore ignore = detail::Ignore(); /* make tuple */ namespace detail { template struct MakeTupleReturnType { using Type = T; }; template struct MakeTupleReturnType> { using Type = T &; }; template struct MakeTupleReturn { using Type = typename MakeTupleReturnType>::Type; }; } template inline Tuple::Type...> make_tuple(T &&...t) { return Tuple::Type...>(forward(t)...); } /* forward as tuple */ template inline Tuple forward_as_tuple(T &&...t) { return Tuple(forward(t)...); } /* tuple relops */ namespace detail { template struct TupleEqual { template bool operator()(const T &x, const U &y) { return TupleEqual()(x, y) && (get(x) == get(y)); } }; template<> struct TupleEqual<0> { template bool operator()(const T &, const U &) { return true; } }; } template inline bool operator==(const Tuple &x, const Tuple &y) { return detail::TupleEqual(x, y); } template inline bool operator!=(const Tuple &x, const Tuple &y) { return !(x == y); } namespace detail { template struct TupleLess { template bool operator()(const T &x, const U &y) { constexpr Size J = TupleSize - I; if (get(x) < get(y)) return true; if (get(y) < get(x)) return false; return TupleLess()(x, y); } }; template<> struct TupleLess<0> { template bool operator()(const T &, const U &) { return true; } }; } template inline bool operator<(const Tuple &x, const Tuple &y) { return detail::TupleLess(x, y); } template inline bool operator>(const Tuple &x, const Tuple &y) { return y < x; } template inline bool operator<=(const Tuple &x, const Tuple &y) { return !(y < x); } template inline bool operator>=(const Tuple &x, const Tuple &y) { return !(x < y); } /* uses alloc */ template constexpr bool UsesAllocator, A> = true; } /* namespace ostd */ #endif