/* TUPLE-HELPER.hpp - metaprogramming utilities for type and data tuples Copyright (C) Lumiera.org 2016, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file tuple.hpp ** Metaprogramming with tuples-of-types and the `std::tuple` record. ** The metaprogramming part of this header complements typelist.hpp and allows ** some additional manipulations on type sequences, especially to integrate ** with the Tuples provided by the standard library. ** ** @see control::CommandDef usage example ** @see TupleHelper_test ** @see typelist.hpp ** @see function.hpp ** @see generator.hpp ** */ #ifndef LIB_META_TUPLE_HELPER_H #define LIB_META_TUPLE_HELPER_H #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-util.hpp" #include "lib/meta/typeseq-util.hpp" #include "lib/meta/util.hpp" #include namespace lib { namespace meta { /** * temporary workaround: * alternative definition of "type sequence", * already using variadic template parameters. * @remarks the problem with our existing type sequence type * is that it fills the end of each sequence with NullType, * which was the only way to get a flexible type sequence * prior to C++11. Unfortunately these trailing NullType * entries do not play well with other variadic defs. * @deprecated when we switch our primary type sequence type * to variadic parameters, this type will be superfluous. */ template struct TySeq { using Seq = TySeq; using List = typename Types::List; }; /** * temporary workaround: additional specialisation for the template * `Prepend` to work also with the (alternative) variadic TySeq. * @see typeseq-util.hpp */ template struct Prepend> { using Seq = TySeq; using List = typename Types::List; }; /** * temporary workaround: strip trailing NullType entries from a * type sequence, to make it compatible with new-style variadic * template definitions. * @note the result type is a TySec, to keep it apart from our * legacy (non-variadic) lib::meta::Types * @deprecated necessary for the transition to variadic sequences */ template struct StripNullType; template struct StripNullType> { using TailSeq = typename StripNullType>::Seq; using Seq = typename Prepend::Seq; }; template struct StripNullType> { using Seq = TySeq<>; // NOTE: this causes the result to be a TySeq }; namespace { // rebinding helper to create std::tuple from a type sequence template struct BuildTupleType : std::false_type { }; template struct BuildTupleType> { using Type = std::tuple; }; /** * temporary workaround: strip trailing NullType entries * prior to rebinding to the `std::tuple` type. */ template struct BuildTupleType> { using VariadicSeq = typename StripNullType>::Seq; using Type = typename BuildTupleType::Type; }; } /** Build a `std::tuple` from types given as type sequence * @remarks for Lumiera, we deliberately use a dedicated template `Types` * to mark a type sequence of types as such. This allows to pass such a * sequence as first-class citizen. The standard library often (ab)uses * the std::tuple for this purpose, which is an understandable, yet * inferior design choice. We should always favour dedicated types * over clever re-use of existing types. */ template using Tuple = typename BuildTupleType::Type; using std::tuple_size; using std::tuple_element; /** match and rebind the type sequence from a tuple */ template struct Types> { using Seq = typename Types::Seq; using List = typename Seq::List; }; /** Hold a sequence of index numbers as template parameters */ template struct IndexSeq { template using AppendElm = IndexSeq; }; /** build an `IndexSeq<0, 1, 2, ..., n-1>` */ template struct BuildIndexSeq { using Ascending = typename BuildIndexSeq::Ascending::template AppendElm; template using FilledWith = typename BuildIndexSeq::template FilledWith::template AppendElm; }; template<> struct BuildIndexSeq<0> { using Ascending = IndexSeq<>; template using FilledWith = IndexSeq<>;; }; #if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////OBSOLETE :: TICKET #988 /** * simple generic Tuple datatype. * Usable both for metaprogramming and as a generic record. * The types within this tuple can either be specified * as Type sequence or as typelist. Default and canonical * is the type-sequence based tuple form \c Tuple> * The secondary from of specifying a tuple is based on a typelist * (and this form is used actually to implement the storage, while * the plain-flat (type sequence based) form acts as interface. * * Irrespective of the flavour actually used, you can always * - get the canonical TupleType (sequence based) * - get the types of head and tail, and a list version of the types * - access the head element and the tail tuple * - access the Nth element and a shifted-b-N sub (tail) tuple */ template struct Tuple; template<> struct Tuple { typedef NullType HeadType; typedef Types<> TailType; typedef Types<> Type; typedef NullType ArgList; typedef Tuple TupleType; typedef Tuple ThisType; typedef Tuple Tail; enum { SIZE = 0 }; NullType& getHead() { return bottom(); } Tail& getTail() { return *this; } Tuple (HeadType const&, Tail const&) { } Tuple () { } template struct ShiftedTuple { typedef Tail Type;}; template Tail& getShifted () { return *this; } template NullType& getAt () { return bottom(); } const NullType getHead_const() const { return bottom(); } const Tail& getTail_const() const { return *this; } TupleType& tupleCast () { return reinterpret_cast (*this); } NullType& bottom() const { return (NullType&) (*this); } }; template struct Tuple > : Tuple { typedef TY HeadType; typedef typename Tuple::Type TailType; typedef typename Prepend::Seq Type; typedef Node ArgList; typedef Tuple TupleType; typedef Tuple ThisType; typedef Tuple Tail; enum { SIZE = count::value }; Tuple ( TY a1 =TY() , Tail tail =Tail() ) : Tail (tail.getHead(), tail.getTail()), val_(a1) { } TY & getHead() { return val_; } Tail& getTail() { return static_cast (*this); } TY const& getHead_const() const { return val_; } Tail const& getTail_const() const { return static_cast (*this); } template class ShiftedTuple { typedef typename Tuple::Type OurType_; typedef typename Shifted::Type ShiftedTypes_; public: typedef Tuple Type; }; template typename ShiftedTuple::Type& getShifted () { typedef typename ShiftedTuple::Type Tail_I; return static_cast (*this); } TupleType& tupleCast () ///< note makes this List-style Tuple appear as plain-flat Tuple { return reinterpret_cast (*this); } template typename Shifted::Head& getAt () { return getShifted().getHead(); } private: TY val_; }; template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 , typename T6 , typename T7 , typename T8 , typename T9 > struct Tuple > : Tuple::List> { typedef T1 HeadType; typedef Types TailType; typedef Types Type; typedef typename Type::List ArgList; typedef Tuple TupleType; typedef Tuple ThisType; typedef Tuple Tail; enum { SIZE = count::value }; /** standard ctor: create from values */ Tuple ( T1 a1 =T1() , T2 a2 =T2() , T3 a3 =T3() , T4 a4 =T4() , T5 a5 =T5() , T6 a6 =T6() , T7 a7 =T7() , T8 a8 =T8() , T9 a9 =T9() ) : Tuple(a1, Tuple(a2,a3,a4,a5,a6,a7,a8,a9)) { } /** shortcut: allow copy construction from a tuple * which is rather defined by a list type */ Tuple (Tuple const& listTuple) : Tuple (listTuple) { } using Tuple::getHead; Tail& getTail() ///< note makes the Tail appear as plain-flat shifted tuple { return Tuple::getTail().tupleCast(); } }; template<> struct Tuple > : Tuple { enum { SIZE = 0 }; typedef Tuple TupNilList; typedef Tuple > ThisType; typedef ThisType Tail; Tuple ( NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() , NullType =NullType() ) { } ///< end recursion of chained ctor calls /** shortcut: allow copy construction from a tuple * which is rather defined by a list type */ Tuple (TupNilList const&) { } }; /** specialisation to shift plain tuple types */ template struct Shifted,i> { typedef typename Shifted::Type Type; typedef typename Shifted::Head Head; typedef Tuple TupleType; }; template struct Shifted, 0> { typedef typename Tuple::Type Type; typedef typename Tuple::HeadType Head; typedef Tuple TupleType; }; /* ====== Helpers for working with Tuples ========= */ namespace tuple { // some convenience access functions template inline typename Shifted::Head& element (TUP& tup) { return tup.template getAt(); } inline Tuple< Types<> > makeNullTuple () { return Tuple > (); } template inline Tuple< Types > make ( T1 a1 ) { return Tuple > (a1); } template< typename T1 , typename T2 > inline Tuple< Types > make ( T1 a1 , T2 a2 ) { return Tuple > (a1,a2); } template< typename T1 , typename T2 , typename T3 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 ) { return Tuple > (a1,a2,a3); } template< typename T1 , typename T2 , typename T3 , typename T4 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 ) { return Tuple > (a1,a2,a3,a4); } template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 , T5 a5 ) { return Tuple > (a1,a2,a3,a4,a5); } template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 , typename T6 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 , T5 a5 , T6 a6 ) { return Tuple > (a1,a2,a3,a4,a5,a6); } template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 , typename T6 , typename T7 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 , T5 a5 , T6 a6 , T7 a7 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7); } template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 , typename T6 , typename T7 , typename T8 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 , T5 a5 , T6 a6 , T7 a7 , T8 a8 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7,a8); } template< typename T1 , typename T2 , typename T3 , typename T4 , typename T5 , typename T6 , typename T7 , typename T8 , typename T9 > inline Tuple< Types > make ( T1 a1 , T2 a2 , T3 a3 , T4 a4 , T5 a5 , T6 a6 , T7 a7 , T8 a8 , T9 a9 ) { return Tuple > (a1,a2,a3,a4,a5,a6,a7,a8,a9); } /** * Helper to construct a new tuple, partially using provided argument values. * Arguments are expected as a tuple, which is assumed to be a sub-tuple of * the target type to be created. The start index of this sub-tuple may be * provided as additional parameter, otherwise it is assumed to be zero, * (i.e. the sub tuple starting left aligned). Any further arguments * of the target type, which aren't covered by the argument tuple, * are default initialised. * @param TYPES type sequence or type list denoting the target tuple type * @param ARGS type sequence of type list denoting the argument tuple type * @param pos start index of the ARGS sequence within the TYPES sequence * * @note call the embedded #create function to invoke * @note when types or positions disagree, argument tuple will be ignored * @see TypeTuple_test#check_build_from_subTuple */ template struct BuildTuple { typedef typename Tuple::TupleType ThisTuple; typedef typename Tuple::ArgList TypeList; typedef typename Tuple::ArgList ArgTypeList; /** * @param the argument values, contained in a list or flat- tuple * of the type denoted by ARGS * @return a plain-flat Tuple instance, initialised with * the values found within arg */ static ThisTuple create (Tuple const& arg) { return BuildTuple ::create(arg) .tupleCast(); } }; template struct BuildTuple, Tuple, pos> ///< tuples allowed instead of plain type sequences/lists : BuildTuple { }; template< typename T , typename TS , typename A , typename AS , uint pos > struct BuildTuple, Node, pos> ///< case: recursion \em before start of arg tuple { typedef Tuple > ThisTuple; typedef Tuple > ThisArg; static ThisTuple create (ThisArg const& arg) { return ThisTuple( T() , BuildTuple, pos-1>::create(arg) ); } }; template< typename A , typename TS , typename AS > struct BuildTuple, Node, 0> ///< case: start of argument tuple detected { typedef Tuple > ThisTuple; typedef Tuple > ThisArg; static ThisTuple create (ThisArg const& arg) { return ThisTuple( arg.getHead_const() , BuildTuple::create (arg.getTail_const()) ); } }; template< typename ARGS , uint i > struct BuildTuple ///< case: hit end of target typelist { typedef Tuple ThisTuple; typedef Tuple ThisArg; static ThisTuple create (ThisArg const&) { return ThisTuple(); } }; template< typename T , typename TS , uint i > struct BuildTuple, NullType, i> ///< case: hit end of argument tuple { typedef Tuple > ThisTuple; typedef Tuple ThisArg; static ThisTuple create (ThisArg const&) { return ThisTuple(); } }; } // (END) access / tuple building helper functions (namespace tuple) /** Trait template for detecting a type tuple */ template class is_Tuple { template struct Check { typedef No_t It; }; template struct Check > { typedef Yes_t It; }; public: static const bool value = (sizeof(Yes_t)==sizeof(typename Check::It)); }; /** Trait template detecting especially tuples * built directly on top of a Typelist */ template class is_TupleListType { template struct Check { enum{ result = sizeof(No_t)}; }; template struct Check > { template Yes_t static check(Node*); Yes_t static check(NullType*); No_t static check(...); enum{ result = sizeof(check( (N*)0)) }; }; public: static const bool value = (sizeof(Yes_t)== Check::result); }; /** Trait template to discern plain tuples and list-type tuples */ template struct is_TuplePlain { static const bool value = is_Tuple::value && !is_TupleListType::value; }; /** Trait template detecting an empty tuple type */ template class is_NullTuple { template struct Check { enum{ result = sizeof(No_t)}; }; template struct Check > { Yes_t static check(Types<>*); Yes_t static check(NullType*); No_t static check(...); enum{ result = sizeof(check( (TY*)0)) }; }; public: static const bool value = (sizeof(Yes_t)== Check::result); }; #endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////OBSOLETE :: TICKET #988 /** * Decorating a tuple type with auxiliary data access operations. * This helper template builds up a subclass of the given TUP (base) type * (which is assumed to be a Tuple or at least need to be copy constructible * from `Tuple` ). The purpose is to use the Tuple as storage record, but * to add a layer of access functions, which in turn might rely on the exact * type of the individual elements within the Tuple. To achieve this, for each * type within the Tuple, the TUP type is decorated with an instance of the * template passed in as template template parameter _X_. Each of these * decorating instances is provided with an index number, allowing to * access "his" specific element within the underlying tuple. * * The decorating template _X_ need to take its own base class as template * parameter. Typically, operations on _X_ will be defined in a recursive fashion, * calling down into this templated base class. To support this, an instantiation * of _X_ with the empty type sequence is generated for detecting recursion end * (built as innermost decorator, i.e. the immediate subclass of TUP) */ template < template class _X_ ///< user provided template , typename TYPES ///< Sequence of types to use within the Accessor , class TUP =Tuple ///< the tuple type to build on , uint i = 0 ///< tuple element index counter > class BuildTupleAccessor { // prepare recursion... using Head = typename Split::Head; using Tail = typename Split::Tail; using NextBuilder = BuildTupleAccessor<_X_, Tail,TUP, i+1>; using NextAccessor = typename NextBuilder::Product; public: /** type of the product created by this template. * Will be a subclass of TUP */ using Product = _X_< Head // the type to use for this accessor , NextAccessor // the base type to inherit from , TUP // the tuple type we build upon , i // current element index >; }; template < template class _X_ , class TUP , uint i > class BuildTupleAccessor< _X_, Types<>, TUP, i> { public: using Product = _X_; // Note: i == tuple size }; }} // namespace lib::meta #endif /*LIB_META_TUPLE_HELPER_H*/