diff --git a/research/try.cpp b/research/try.cpp index 5b1387ae2..b5f66742a 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -53,7 +53,7 @@ typedef unsigned int uint; #include "lib/format-cout.hpp" #include "lib/format-util.hpp" //#include "lib/meta/function.hpp" -#include "lib/meta/tuple-helper.hpp" +#include "lib/meta/variadic-helper.hpp" #include "lib/test/test-helper.hpp" #include diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index 9f91650e6..6d16cbf6f 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -27,6 +27,17 @@ ** 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 @@ -68,7 +79,7 @@ namespace meta { * 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. + * to variadic parameters, this type will be obsoleted. */ template struct TySeq diff --git a/src/lib/meta/typelist.hpp b/src/lib/meta/typelist.hpp index 53474572e..ddb0c6f21 100644 --- a/src/lib/meta/typelist.hpp +++ b/src/lib/meta/typelist.hpp @@ -55,6 +55,17 @@ This code is heavily inspired by ** effectively this is a flavour of functional programming. Just the ** "execution environment" is the compiler, during compilation. ** + ** @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 lib::visitor::Applicable usage example ** @see control::CommandSignature more elaborate usage example (dissecting a functor signature) ** @see TypeList_test diff --git a/src/lib/meta/typeseq-util.hpp b/src/lib/meta/typeseq-util.hpp index cab1eb686..f6c9effc2 100644 --- a/src/lib/meta/typeseq-util.hpp +++ b/src/lib/meta/typeseq-util.hpp @@ -31,6 +31,17 @@ ** - shifting a type sequence ** - re-generating a type sequence from a typelist. ** + ** @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 typeseq-manip-test.cpp ** @see typelist.hpp ** @see typelist-util.hpp diff --git a/src/lib/meta/variadic-helper.hpp b/src/lib/meta/variadic-helper.hpp new file mode 100644 index 000000000..ab1e79d0c --- /dev/null +++ b/src/lib/meta/variadic-helper.hpp @@ -0,0 +1,495 @@ +/* + VARIADIC-HELPER.hpp - metaprogramming utilities for parameter- and type sequences + + 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 variadic-helper.hpp + ** Metaprogramming with type sequences based on variadic template parameters. + ** The type rebinding- and helper templates in this header allow to perform + ** simple sequence manipulations on sequences of template parameters extracted + ** from variadic parameter packs. The goal is to (pre)process flexible argument + ** lists _at compile time,_ driven by template instantiation, allowing to specialise + ** and react specifically on some concrete pattern of argument types. + ** + ** @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_VARIADIC_HELPER_H +#define LIB_META_VARIADIC_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 util { // forward declaration + + template + std::string + toString (TY const& val) noexcept; +} + + +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 obsoleted. + */ + 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; + }; + + 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; + + + + /** match and rebind the type sequence from a tuple */ + template + struct Types> + { + 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 + { }; + + + + + + /** 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 OffsetBy = typename BuildIndexSeq::template OffsetBy::template AppendElm; + + template + using FilledWith = typename BuildIndexSeq::template FilledWith::template AppendElm; + }; + + template<> + struct BuildIndexSeq<0> + { + using Ascending = IndexSeq<>; + + template + using OffsetBy = IndexSeq<>; + + template + using FilledWith = IndexSeq<>; + }; + + + + /** build an index number sequence from a structured reference type */ + template + struct IndexIter; + + /** build an index number sequence from a type sequence */ + template + struct IndexIter> + { + /////TODO as long as Types is not variadic (#987), we need to strip NullType here (instead of just using sizeof...(TYPES) + enum {SIZ = lib::meta::count::List>::value }; + + using Seq = typename BuildIndexSeq::Ascending; + }; + + + + + + + /** + * 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 + { + using TypeIdxIterator = typename IndexIter::Seq; + + protected: + template + TupleConstructor (SRC values, IndexSeq*) + : Tuple (_ElmMapper_, idx>{values}...) + { } + + + public: + template + TupleConstructor (SRC values) + : TupleConstructor (values, (TypeIdxIterator*)nullptr) + { } + }; + + + /** + * 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_VARIADIC_HELPER_H*/