/* VARIADIC-HELPER.hpp - metaprogramming utilities for parameter- and type sequences Copyright (C) 2016, Hermann Vosseler   **Lumiera** 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. See the file COPYING for further details. */ /** @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. ** ** @remark in Lumiera, over time three different approaches were developed for ** handling sequences of types in metaprogramming; some of these techniques ** are better suited for specific kinds of tasks than others ** - templates with variadic arguments (e.g. std::tuple) can be manipulated directly ** - a type-sequence `Types` can be primed / rebound from other variadic templates ** - Loki-style type-lists are created from type-sequences and enable elaborate manipulations ** @see feed-manifold.hpp advanced usage example in the Render Engine ** @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/variadic-rebind.hpp" #include "lib/meta/util.hpp" namespace lib { namespace meta { /* ==== Build Variadic Sequences ==== **/ /** Hold a sequence of index numbers as template parameters */ template struct IndexSeq { template using AppendElm = IndexSeq; template using PrependElm = IndexSeq; }; /** * build regular sequences of index number * e.g. `IndexSeq<0, 1, 2, ..., n-1>` */ template struct BuildIndexSeq { using Ascending = typename BuildIndexSeq::Ascending::template AppendElm; using Descending = typename BuildIndexSeq::Descending::template PrependElm; template using OffsetBy = typename BuildIndexSeq::template OffsetBy::template AppendElm; template using FilledWith = typename BuildIndexSeq::template FilledWith::template AppendElm; template using First = typename BuildIndexSeq::Ascending; template using After = typename BuildIndexSeq< (n>c)? n-c : 0>::template OffsetBy; }; template<> struct BuildIndexSeq<0> { using EmptySeq = IndexSeq<>; using Ascending = EmptySeq; using Descending = EmptySeq; template using OffsetBy = EmptySeq; template using FilledWith = EmptySeq; template using First = EmptySeq; template using After = EmptySeq; }; /** * build a sequence of index numbers based on a type sequence */ template struct BuildIdxIter { enum {SIZ = sizeof...(TYPES) }; using Builder = BuildIndexSeq; using Ascending = typename Builder::Ascending; using Descending = typename Builder::Descending; template using OffsetBy = typename Builder::template OffsetBy; template using FilledWith = typename Builder::template FilledWith; template using First = typename Builder::template First; template using After = typename Builder::template After; }; /** build an index number sequence from a type sequence */ template struct BuildIdxIter> : BuildIdxIter { }; /* ==== Build and Rebuild variadic type sequences ==== **/ /** * Variadic type sequence builder. * This metaprogramming helper template provides an unified view * to handle _»tuple-like« types and variadic _type sequences._ * - the constant #SIZ gives the number of elements * - the nested type #Idx can be used as _index sequence_ * - #Seq is a _variadic type sequence_ with the extracted types * - #Tup is a std::tuple over these types * - the nested template #Apply wraps each type into another template * - #Rebind likewise instantiates another template with the element types * - #AndAll applies a predicate and combines the result with _logical and_ * - #OrAll similarly evaluates _logical or_ on the application results */ template struct ElmTypes { static constexpr size_t SIZ = 1; using Idx = std::index_sequence; using Seq = Types; using Tup = std::tuple; template class META> using Apply = Types>; template class O> using Rebind = O; template class PRED> using AndAll = std::__and_>; template class PRED> using OrAll = std::__or_>; }; /** Partial specialisation to handle type sequences */ template struct ElmTypes> { static constexpr size_t SIZ = sizeof...(TYPES); using Idx = std::make_index_sequence; using Seq = Types; using Tup = std::tuple; template class META> using Apply = Types...>; template class O> using Rebind = typename lib::meta::RebindVariadic::Type; template class PRED> using AndAll = typename ElmTypes>::template Rebind; template class PRED> using OrAll = typename ElmTypes>::template Rebind; }; // Note: a further specialisation for any »tuple-like« is defined in tuple-helper.hpp /* ==== Invoke with index from variadic ==== **/ /** helper to invoke a functor, passing instances of std::integral_constant * @tparam N size of the index-sequence to use for instantiation * @remark the functor is given for... * - to be invoked either (as void) for each index * - or as a predicate, combining the results with AND / OR */ template class WithIdxSeq { template static constexpr void invoke_forEach (FUN&& fun, std::index_sequence) { (fun (std::integral_constant{}), ...); } template static constexpr bool and_forEach (FUN&& fun, std::index_sequence) { return (fun (std::integral_constant{}) and ...); } template static constexpr bool or_forEach (FUN&& fun, std::index_sequence) { return (fun (std::integral_constant{}) or ...); } using IdxSeq = std::make_index_sequence; public: template static constexpr void invoke (FUN&& fun) { invoke_forEach (std::forward(fun), IdxSeq{}); } template static constexpr bool andAll (FUN&& fun) { return and_forEach (std::forward(fun), IdxSeq{}); } template static constexpr bool orAny (FUN&& fun) { return or_forEach (std::forward(fun), IdxSeq{}); } }; /** * Invoke a function (or λ) with index numbers derived from some variadic count. * Notably this construct can be used for compile-time iteration over a structure. * Instances of `std::integral_constant` are passed in sequence to the functor. * The _size_ of the index sequence is derived using a \ref ElmTypes specialisation * - if the type \a TTX is _tuple-like,_ then std::tuple_size is used * - otherwise, if the type is a _type sequence_ (`Types`), then * the size of this meta sequence is used * - otherwise, sequence-size ≡ 1 is used as fall-back */ template inline void forEachIDX (FUN&& fun) { WithIdxSeq::SIZ>::invoke (std::forward (fun)); } template inline bool andAllIDX (FUN&& fun) { return WithIdxSeq::SIZ>::andAll (std::forward (fun)); } template inline bool orAnyIDX (FUN&& fun) { return WithIdxSeq::SIZ>::orAny (std::forward (fun)); } /* ==== Manipulation of variadic arguments ==== **/ namespace { // Implementation delegate template... /** * @internal pick a single argument from a variadic parameter pack * @tparam i the index number (zero based) of the argument to select * @warning i must be smaller than the number of arguments available */ template struct SelectVararg { template static auto get (ARG, ARGS&& ...args) { return SelectVararg::get (std::forward (args)...); } }; template<> struct SelectVararg<0> { template static auto get (ARG&& a, ARGS...) { return std::forward(a); } }; /** * @internal helper to decide if SelectVararg shall be applied. * When the boolean condition does not hold, then, instead of selecting * from the argument list, an element of type DEFAULT is created as fallback. */ template struct SelectOrInit : SelectVararg { }; template struct SelectOrInit { template static DEFAULT get (ARGS&&...) { return DEFAULT{}; } }; }//(End)Implementation /** * Helper to single out one argument from a variadic argument pack. * @tparam idx the index number (zero based) of the argument to select * @remark typically this function is used "driven" by an likewise variadic index sequence, * where the index sequence itself is picked up by a pattern match; this usage pattern * allows arbitrarily to handle some of the arguments of a variable argument list, * as determined by the index sequence passed in. */ template constexpr inline auto pickArg (ARGS&&... args) { static_assert (idx < sizeof...(args), "insufficient number of arguments"); return SelectVararg::get (std::forward (args)...); } /** * Helper to pick one initialisation argument from a variadic argument pack, * falling back to a default constructed element of type `DEFAULT` in case of * insufficient number of variadic arguments. * @tparam idx the index number (zero based) of the argument to select * @tparam DEFALUT type of the default element to construct as fallback */ template constexpr inline auto pickInit (ARGS&&... args) { return SelectOrInit<(idx < sizeof...(args)), DEFAULT, idx>::get (std::forward (args)...); } }} // namespace lib::meta #endif /*LIB_META_VARIADIC_HELPER_H*/