/* 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-helper.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. ** ** @warning the metaprogramming part of Lumiera to deal with type sequences is in a ** state of transition, since C++11 now offers direct language support for ** processing of flexible template parameter sequences ("parameter packs"). ** It is planned to regroup and simplify our homemade type sequence framework ** to rely on variadic templates and integrate better with std::tuple. ** It is clear that we will _retain some parts_ of our own framework, ** since programming with _Loki-style typelists_ is way more obvious ** and straight forward than handling of template parameter packs, ** since the latter can only be rebound through pattern matching. ** @todo transition lib::meta::Types to variadic parameters /////////////////////////////////TICKET #987 ** ** @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/variadic-helper.hpp" #include "lib/meta/util.hpp" #include namespace util { // forward declaration template std::string toString (TY const& val) noexcept; } namespace lib { namespace meta { 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; }; template struct BuildTupleType> { using Seq = typename Types< Node>::Seq; using Type = typename BuildTupleType::Type; }; template<> struct BuildTupleType { 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; /** temporary workaround: match and rebind the type sequence from a tuple */ template struct RebindTySeq { using Seq = typename Types::Seq; using List = typename Seq::List; }; template struct RebindTySeq> { using Seq = typename Types::Seq; using List = typename Seq::List; }; /** trait to detect tuple types */ template struct is_Tuple : std::false_type { }; template struct is_Tuple> : std::true_type { }; /** * Extensible Adapter to construct a distinct tuple from some arbitrary source type. * This includes the possibility to re-map elements or element positions. * @tparam TYPES sequence of types to use for the tuple * @tparam _ElmMapper_ a _template_ to extract each * constructor argument from the source value. * On invocation, we'll pick up the source type from the actual ctor argument, * and then invoke this helper template iteratively for each component of the * tuple, passing as template arguments * - the source type, as picked up from the constructor * - the target tuple type, i.e. `Tuple` * - the actual index position of the tuple element * to be initialised through this concrete instantiation. * @remarks this design has several extension points. Pretty much any conceivable * initialisation logic can be embodied in the `_ElmMapper_` template. The sole * requirement is that the concrete instance is _assignable_ by the source type * and _convertible_ to the individual member type of the target tuple it is * invoked for. Moreover, it is possible to build a generic _element extractor_, * which will be specialised on base of the source type accepted. * @see ExtractArg */ template< typename TYPES , template class _ElmMapper_ > struct TupleConstructor : Tuple { /** meta-sequence to drive instantiation of the ElmMapper */ using SequenceIterator = typename BuildIdxIter::Ascending; protected: template TupleConstructor (SRC values, IndexSeq) : Tuple (_ElmMapper_, idx>{values}...) { } public: template TupleConstructor (SRC values) : TupleConstructor (values, SequenceIterator()) { } }; /** * Generic converter to somehow extract values from the "source" * type to fill and initialise a tuple of given target type. * @note to be specialised. The concrete specialisation is * assumed to provide a _member template_ `Access`, * which in turn picks and converts the value for the n-th * tuple element. */ template struct ElementExtractor; template using ExtractArg = typename ElementExtractor::template Access; /** * convenience shortcut to build a tuple from some suitable source data. * For this to work, there needs to be a partial specialisation for * (\ref ElementExtractor) to deal with the concrete source type given. * @note we provide such a specialisation for `Record`, which * allows us to fill an (argument) tuple from a sequence of generic * data values, with run-time type compatibility check. * @see tuple-record-init.hpp */ template Tuple buildTuple (SRC values) { return TupleConstructor (values); } /** * 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 }; /** * Helper to dump tuple contents. * Defined to act as "Accessor" for BuildTupleAccessor, this helper template * allows to create a recursive operation to invoke string conversion on * all elements within any given tuple. */ template < typename TY , class BASE , class TUP , uint idx > struct TupleElementDisplayer : BASE { using BASE::BASE; std::string dump (std::string const& prefix ="(") const { return BASE::dump (prefix + util::toString(std::get(*this))+","); } }; template struct TupleElementDisplayer : TUP { TupleElementDisplayer (TUP const& tup) : TUP(tup) { } std::string dump (std::string const& prefix ="(") const { if (1 < prefix.length()) // remove the trailing comma return prefix.substr (0, prefix.length()-1) +")"; else return prefix+")"; } }; /** * convenience function to dump a given tuple's contents. * Using the BuildTupleAccessor, we layer a stack of Instantiations of * the TupleElementDisplayer temporarily on top of the given tuple, * just to invoke a recursive call chain through this layers * and get a string representation of each element in the * tuple. */ template inline std::string dump (std::tuple const& tuple) { using BuildAccessor = BuildTupleAccessor>; using Displayer = typename BuildAccessor::Product ; return static_cast (tuple) .dump(); } }} // namespace lib::meta // add a specialisation to enable tuple string conversion namespace util { template struct StringConv> { static std::string invoke (std::tuple const& tuple) noexcept try { return "«"+typeStr(tuple) + "»──" + lib::meta::dump (tuple); } catch(...) { return FAILURE_INDICATOR; } }; } // namespace util #endif /*LIB_META_TUPLE_HELPER_H*/