LUMIERA.clone/src/lib/meta/variadic-helper.hpp
Ichthyostega 49f2e34e4c Library: extract type rebinding helper
...turns out this is entirely generic and not tied to the context
within ActivityDetector, where it was first introduced to build a
mock functor to log all invocations.

Basically this meta-function generates a new instantiation of the
template X, using the variadic argument pack from template U<ARGS...>
2023-08-01 14:52:20 +02:00

379 lines
12 KiB
C++

/*
VARIADIC-HELPER.hpp - metaprogramming utilities for parameter- and type sequences
Copyright (C) Lumiera.org
2016, Hermann Vosseler <Ichthyostega@web.de>
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"
namespace lib {
namespace meta {
//////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #987 temporary WORKAROUND -- to be obsoleted
/**
* 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. ////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic
*/
template<typename...TYPES>
struct TySeq
{
using Seq = TySeq;
using List = typename Types<TYPES...>::List;
};
/**
* temporary workaround: additional specialisation for the template
* `Prepend` to work also with the (alternative) variadic TySeq.
* @see typeseq-util.hpp
*/
template<typename T, typename...TYPES>
struct Prepend<T, TySeq<TYPES...>>
{
using Seq = TySeq<T, TYPES...>;
using List = typename Types<T, 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 ////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic
*/
template<typename SEQ>
struct StripNullType;
template<typename T, typename...TYPES>
struct StripNullType<Types<T,TYPES...>>
{
using TailSeq = typename StripNullType<Types<TYPES...>>::Seq;
using Seq = typename Prepend<T, TailSeq>::Seq;
};
template<typename...TYPES>
struct StripNullType<Types<NullType, TYPES...>>
{
using Seq = TySeq<>; // NOTE: this causes the result to be a TySeq
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #987 temporary WORKAROUND(End) -- to be obsoleted
//////////////////////////TICKET #943 : temporary workaround until full C++14 compliance (need constexpr)
template<typename X>
constexpr inline X const&
max (X const& a, X const& b)
{
return a < b? b : a;
}
template<typename X>
constexpr inline X const&
min (X const& a, X const& b)
{
return b < a? b : a;
}
//////////////////////////TICKET #943 : temporary workaround until full C++14 compliance (need constexpr)
/* ==== Build Variadic Sequences ==== **/
/** Hold a sequence of index numbers as template parameters */
template<size_t...idx>
struct IndexSeq
{
template<size_t i>
using AppendElm = IndexSeq<idx..., i>;
template<size_t i>
using PrependElm = IndexSeq<i, idx...>;
};
/**
* build regular sequences of index number
* e.g. `IndexSeq<0, 1, 2, ..., n-1>`
*/
template<size_t n>
struct BuildIndexSeq
{
using Ascending = typename BuildIndexSeq<n-1>::Ascending::template AppendElm<n-1>;
using Descending = typename BuildIndexSeq<n-1>::Descending::template PrependElm<n-1>;
template<size_t d>
using OffsetBy = typename BuildIndexSeq<n-1>::template OffsetBy<d>::template AppendElm<n-1+d>;
template<size_t x>
using FilledWith = typename BuildIndexSeq<n-1>::template FilledWith<x>::template AppendElm<x>;
template<size_t c>
using First = typename BuildIndexSeq<min(c,n)>::Ascending;
template<size_t c>
using After = typename BuildIndexSeq< (n>c)? n-c : 0>::template OffsetBy<c>;
};
template<>
struct BuildIndexSeq<0>
{
using Empty = IndexSeq<>;
using Ascending = Empty;
using Descending = Empty;
template<size_t>
using OffsetBy = Empty;
template<size_t>
using FilledWith = Empty;
template<size_t>
using First = Empty;
template<size_t>
using After = Empty;
};
/**
* build a sequence of index numbers based on a type sequence
*/
template<typename...TYPES>
struct BuildIdxIter
{
enum {SIZ = sizeof...(TYPES) };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
template<size_t c>
using First = typename Builder::template First<c>;
template<size_t c>
using After = typename Builder::template After<c>;
};
/** build an index number sequence from a type sequence */
template<typename...TYPES>
struct BuildIdxIter<Types<TYPES...>>
{
///////////////////////TICKET #987 : since Types<T...> is not variadic, need to strip NullType here (instead of just using sizeof...(TYPES)
enum {SIZ = lib::meta::count<typename Types<TYPES...>::List>::value };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
template<size_t c>
using First = typename Builder::template First<c>;
template<size_t c>
using After = typename Builder::template After<c>;
};
/* ==== Rebinding Variadic Arguments ==== **/
/**
* Metaprogramming helper to transfer variadic arguments.
* - builds a new type instantiation from the Template \a X
* - possibly picks up the variadic argument pack from a given
* source template `U<ARGS....>`
* @tparam X a variadic template
*/
template<template<typename...> class X, typename...ARGS>
struct RebindVariadic
{
using Type = X<ARGS...>;
};
template<template<typename...> class X
,template<typename...> class U
,typename...ARGS>
struct RebindVariadic<X, U<ARGS...>>
{
using Type = X<ARGS...>;
};
/* ==== 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<size_t i>
struct SelectVararg
{
template<typename ARG, typename...ARGS>
static auto
get (ARG, ARGS&& ...args)
{
return SelectVararg<i-1>::get (std::forward<ARGS> (args)...);
}
};
template<>
struct SelectVararg<0>
{
template<typename ARG, typename...ARGS>
static auto
get (ARG&& a, ARGS...)
{
return std::forward<ARG>(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<bool, typename, size_t idx>
struct SelectOrInit
: SelectVararg<idx>
{ };
template<typename DEFAULT, size_t idx>
struct SelectOrInit<false, DEFAULT, idx>
{
template<typename...ARGS>
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<size_t idx, typename...ARGS>
constexpr inline auto
pickArg (ARGS&&... args)
{
static_assert (idx < sizeof...(args), "insufficient number of arguments");
return SelectVararg<idx>::get (std::forward<ARGS> (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<size_t idx, typename DEFAULT, typename...ARGS>
constexpr inline auto
pickInit (ARGS&&... args)
{
return SelectOrInit<(idx < sizeof...(args)), DEFAULT, idx>::get (std::forward<ARGS> (args)...);
}
}} // namespace lib::meta
#endif /*LIB_META_VARIADIC_HELPER_H*/