/*!
* \file typelist.h
* \brief A template parameter "container"
*
* Copyright (C) 2018-2019 Christos Choutouridis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#ifndef __utl_meta_pack_h__
#define __utl_meta_pack_h__
#include
#include
#include
#include
#include
#include
/*!
* \ingroup meta
* \defgroup typelist
*/
//! @{
namespace utl {
namespace meta {
/*!
* \brief
* A class template that just holds a parameter pack.
*
* The idea came from MPL's sequence concept[1] and from N4115[2].
* In addition to N4115's name "packer" we just prefer a name which is object, not a subject.
* This way the name gives the feeling of a container and smells like Python.
*
* In addition to tuple we lack members, so typelist could serve as an empty base class,
* and an object of the ultimate type could always be instantiated
* (even if the parameter typelist contains void or some type that lacks
* a default constructor).
* \example
* \code
* using l1 = typelist;
* l1 a {};
* \endcode
*
* boost::hana[3] suggests a more powerful scheme were type invariant structures can be used
* for metaprograming also. This lib does not need (yet) this kind of power (we afraid the
* responsibility that comes along). So a simple python-like list with some extra vector-like
* element access functionalities and no iterators is good enough(for now).
*
* [1]: https://www.boost.org/doc/
* [2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4115.html
* [3]: https://github.com/boostorg/hana
*/
template
struct typelist {
using type = typelist; //!< act as identity
//! \return sizeof...(Ts)
static constexpr size_t size() noexcept {
return sizeof...(Ts);
}
//! \return true if empty
static constexpr bool empty() noexcept {
return (sizeof...(Ts) == 0);
}
// ======= times utility =======
private:
template struct cat_ { };
template
struct cat_, typelist> {
using type = typelist;
};
template
struct times_ {
//static_assert( N >= 0, "Cannot make typelist of negative length" );
using type = eval<
cat_<
eval>,
eval>
>
>;
};
template
struct times_<1, T...> {
using type = typelist;
};
template
struct times_<0, T...> {
using type = typelist<>;
};
public:
/*!
* Generate typelist of size \c N arguments.
* \example
* \code
* static_assert (
* std::is_same::times<2>,
* typelist
* >, "" );
* \endcode
* complexity \f$ O(logN) \f$
*/
template
using times = eval<
times_
>;
};
/*!
* An integral constant wrapper that is the size of the \c meta::typelist
*
* Complexity \f$ O(1) \f$.
*
* \param List A typelist
* \return The size of the typelist
*/
template
using size = size_;
/*!
* An Boolean constant wrapper that returns if the typelist is empty
*
* Complexity \f$ O(1) \f$.
*
* \param List A typelist
* \return Empty or not
*/
template
using empty = bool_;
//! pair
//! A special typelist with only 2 Types
template
using pair = typelist;
//! repeat
//! @{
/*!
* A wrapper to typelist<>::times<> utility for integer argument \p N
*/
template
using repeat_c = typename typelist::template times;
/*!
* A wrapper to typelist<>::times<> utility for integral_c argument \p N
*/
template
using repeat = repeat_c;
//! @}
/*!
* Apply
* An analogous to apply() implementation for tuples. We just use
* Our typelist<> and integer_sequence<> types.
*/
//! @{
namespace apply_impl {
template
struct apply_ { };
//! \p Sequence == typelist<>
template
struct apply_> {
using type = invoke;
};
//! Sequence == integer_sequence<>
template
struct apply_> {
using type = invoke...>;
};
}
/*!
* Apply the Invocable \p Fn using the types in the type \p Seq as arguments.
* \note
* This is the opposed operation of typelist
*
* If \p Seq == typelist<> then
* Unpack typelist and apply to \c Fn
* It \p Seq == integer_sequence<> then
* Unpack and use the integral_c<> of each integer
*/
template
using apply = apply_impl::apply_;
//! @}
/*
* ========= element access ========
*/
//! at: random element access
//! @{
namespace at_impl {
template struct _as_pointer__ { using type = T*; };
template struct _as_pointer__ { using type = T*; };
template using as_pointer_ = eval<
_as_pointer__
>;
template
struct at_head_ { };
template
struct at_head_ > {
// successful selection N voids, one T* and the rest
template static constexpr T select(voids..., T*, ...);
// selection on error
static constexpr nil_ select (...);
};
template
struct at_ { };
template
struct at_, N> {
using head_ = at_head_::times>; //< make at_head_<> with N void*
using type = decltype(
head_::select(static_cast>(nullptr)...) //< pass all as List*...
);
};
}
/*!
* Return the \p N th element in the \c meta::typelist \p List.
*
* Complexity \f$ O(logN) \f$.
*/
template
using at_c = eval<
at_impl::at_
>;
/*!
* Return the \p N th element in the \c meta::typelist \p List.
*
* Complexity \f$ O(N) \f$.
*/
template
using at = at_c;
//!@}
//! front
//! @{
namespace front_impl {
template
struct front_ { };
template
struct front_> {
using type = Head;
};
}
//! Return the first element in \c meta::typelist \p List.
//! Complexity \f$ O(1) \f$.
template
using front = eval<
front_impl::front_
>;
//! @}
//! back
//! @{
namespace back_impl {
template
struct back_ { };
template
struct back_> {
using type = at_c <
typelist, sizeof...(Tail)
>;
};
}
//! Return the last element in \c meta::typelist \p List.
//! Complexity \f$ O(N) \f$.
template
using back = eval<
back_impl::back_
>;
//! @}
/*
* ========= typelist operations =========
*/
//! Concatenation
//! @{
namespace cat_impl {
template
struct cat_ { };
template <>
struct cat_<> {
using type = typelist<>;
};
template
struct cat_> {
using type = typelist;
};
template
struct cat_, typelist> {
using type = typelist;
};
template
struct cat_, typelist, Ln...>
: cat_ , Ln...> { };
}
/*!
* Transformation that concatenates several lists into a single typelist.
* The parameters must all be instantiations of \c meta::typelist.
* Complexity: \f$ O(N) \f$
* where \f$ N \f$ is the number of lists passed to the algorithm.
*/
template
using cat = eval<
cat_impl::cat_
>;
//! @}
//! fold, rev_fold
//! @{
namespace fold_impl {
// fold<, V, F> == F, T2>, T3>
template
struct fold_ { }; // ill formed
// recursive call
template
struct fold_, V, Fn> {
// recursive call of fold_ by consuming typelist and invoking Fn
using type = eval<
fold_<
typelist,
invoke,
Fn
>
>;
};
// termination call
template
struct fold_, V0, Fn> {
using type = V0;
};
}
/*!
* transform the \p List to a new one by doing a left fold using binary Invocable \p Fn
* and initial value \p V
* Complexity \f$ O(N) \f$
* \example
* fold, V, F> == F, T2>, T3>
* \example
* fold, V, F> == V
* \param List The list to fold
* \param V The initial item feeded to Fn
* \param Fn The binary Invocable
*/
template
using fold = eval>;
//! accumulate is an stl name for fold
template
using accumulate = fold;
namespace rev_fold_impl {
// rev_fold<, V, F> == F>>
template
struct rev_fold_ { }; // ill formed
// recursive call
template
struct rev_fold_, V, Fn> {
// recursive call inside invoke. This way the 2nd argument of Fn
// becoming the recursive "thing", inside Fn<>
using type = invoke <
Fn, Head, eval<
rev_fold_ <
typelist,
V,
Fn
>>
>;
};
// pre-termination call
template
struct rev_fold_ , V, Fn> {
using type = invoke;
};
// termination call
template
struct rev_fold_ , V, Fn> {
using type = V;
};
}
/*!
* transform the \p List to a new one by doing a right fold using binary Invocable \p Fn
* and initial value \p V
* Complexity \f$ O(N) \f$
* \example
* rev_fold, V, F> == F>>
* \example
* rev_fold, V, F> == V
* \param List The list to fold
* \param V The initial item fed to Fn
* \param Fn The binary Invocable
*/
template
using rev_fold = eval<
rev_fold_impl::rev_fold_
>;
//! @}
/*!
* Return a new \c typelist by adding the elements \p Ts to the front of \p List.
* Complexity \f$ O(1) \f$
*/
template
using push_front = eval<
apply <
bind_front, Ts...>, List
>
>;
/*!
* Return a new \c typelist by adding the elements \p Ts to the back of \p List.
* Complexity \f$ O(1) \f$
*/
template
using push_back = eval<
apply <
bind_back, Ts...>, List
>
>;
//! reverse
//! @{
namespace reverse_impl {
template >
struct reverse_ {
using type = fold>;
};
}
/*!
* Return a new \c typelist by reversing the elements in the list \p List.
* Complexity \f$ O(N) \f$
*/
template
using reverse = eval<
reverse_impl::reverse_
>;
//! @}
//! pop_front
//! @{
namespace pop_front_impl {
template
struct pop_front_ { };
template
struct pop_front_> {
using type = typelist;
};
}
/*!
* Return a new \c typelist by removing the first element from the
* front of \p List.
* Complexity \f$ O(1) \f$
*/
template
using pop_front = eval<
pop_front_impl::pop_front_
>;
//! @}
//! pop_back
//! @{
namespace pop_back_impl {
template
struct pop_back_ {
using type = reverse<
pop_front>
>;
};
}
/*!
* Return a new \c typelist by removing the last element from the \p List.
* Complexity \f$ O(N) \f$.
* \note
* This operation, in addition from other push/pop operations, is
* heavy(2 reverse operations).
*/
template
using pop_back = eval <
pop_back_impl::pop_back_
>;
//! @}
//! Transform
//! @{
namespace transform_impl {
template
struct transform_ { };
template
struct transform_, Fn>,
void_t...> > /* SFINAE check */ {
using type = typelist<
invoke_t...
>;
};
template
struct transform_, typelist, Fn>,
void_t...>> /* SFINAE check */ {
using type = typelist<
invoke_t...
>;
};
}
/*!
* Transformation by applying an invocable \c Fn to one or two lists
* and return the resulting typelist
*
* Complexity \f$ O(N) \f$.
* \example
* \code
* using l1 = typelist;
* using l2 = typelist;
* using r1 = transform; // F1, unary invocable
* using r2 = transform; // F2, binary invocable
* \endcode
*/
template
using transform = eval<
transform_impl::transform_>
>;
//! @}
//! Transform lazy
//! @{
namespace transform_lazy_impl {
template
struct transform_lazy_ { };
// Match for Unary Fn with one typelist
template
struct transform_lazy_, Fn>,
void_t...> > /* SFINAE check */ {
using type = typelist<
invoke...
>;
};
// Match for Binary Fn with two typelists
template
struct transform_lazy_, typelist, Fn>,
void_t...>> /* SFINAE check */ {
using type = typelist<
invoke...
>;
};
}
/*!
* Transformation by applying an invocable \c Fn to one or two lists
* and return a typelist containing the invocables with their arguments,
* not their resulting types.
*
* Complexity \f$ O(N) \f$
*
* \example
* \code
* using l1 = typelist;
* using l2 = typelist;
* using r1 = transform; // F1, unary invocable
* using r2 = transform; // F2, binary invocable
* \endcode
*/
template
using transform_lazy = eval<
transform_lazy_impl::transform_lazy_>
>;
//! @}
//! find_if, find
//! @{
namespace find_if_impl {
template
struct find_if_ { };
template
struct find_if_, Fn, N> {
// Recursive call to find_if_ until Fn returns true_
using type = if_ <
invoke_t,
index_, // done, return current index
eval, Fn, N+1>
>
>;
};
// When empty or when we are one place after the last item return Npos
template
struct find_if_, Fn, N> {
using type = Npos;
};
}
/*!
* Search for the first \c Item on the \p List for which the predicate \p Pred
* returns true_ when `eval>`
*
* Complexity \f$ O(N) \f$
*
* \param List A typelist
* \param Pred A Unary invocable predicate
* \return An integral constant of index_t with the location of the first match,
* or Npos otherwise.
*/
template
using find_if = eval<
find_if_impl::find_if_
>;
/*!
* Search for the first occurrence of type \p T on a \p List
*/
template
using find = find_if>;
//! @}
//! seek_if
//! @{
namespace seek_if_impl {
template
struct seek_if_ { };
template
struct seek_if_, Fn, N> {
// recursive call to seek_if_ until Fn returns true_
using type = if_ <
invoke_t,
typelist, // done, return the typelist starting from here
eval, Fn, N+1>
>
>;
};
// When empty or when we are one place after the last item return empty typelist
template
struct seek_if_, Fn, N> {
using type = typelist<>;
};
}
/*!
* Search for the first \c Item on the \p List for which the predicate \p Pred
* returns true_ when `eval>` and return the rest of the \p List
* starting from that position as new typelist
*
* Complexity \f$ O(N) \f$
*
* \param List A typelist
* \param Pred A Unary invocable predicate
* \return An integral constant with the location of the first match, on Npos otherwise
*/
template
using seek_if = eval<
seek_if_impl::seek_if_
>;
/*!
* Search for the first \c Item on the \p List of type \p T and return the rest
* of the \p List starting from that position as new typelist
*/
template
using seek = seek_if >;
//! @}
//! count_if
//! @{
namespace count_if_impl {
template
struct count_if_ { };
template
struct count_if_, Fn, N> {
// Recursive call to count_if_ up to the end of List, counting all invokes of Fn
// returning true_
using type = if_ <
invoke_t,
eval<
count_if_, Fn, N+1> // increase and re-call
>,
eval<
count_if_, Fn, N> // re-call without increasing
>
>;
};
// At the end of the List return the counter
template
struct count_if_, Fn, N> {
using type = size_;
};
}
/*!
* Count all \c Items on the \p List for which the predicate \p Pred
* returns true_ when `eval>`
*
* Complexity \f$ O(N) \f$
*
* \param List A typelist
* \param Pred A Unary invocable predicate
* \return The total count of occurrences as an integral constant of size_t
*/
template
using count_if = eval<
count_if_impl::count_if_
>;
/*!
* Count all occurrences of type \p T int \p List
*/
template
using count = count_if>;
//! @}
//! filter
//! @{
namespace filter_impl {
template
struct filter_ { };
template
struct filter_, Fn, L> {
// Recursive call to filter_ up to the end of the List, creating a new list
// of items for which the invoke of Fn returns true_
using type = if_ <
invoke_t ,
eval, Fn, cat>>>, // Add the element and re-call
eval, Fn, L>> // re-call with the same list
>;
};
// At the end return the produced list
template
struct filter_, Fn, L> {
using type = L;
};
}
/*!
* Return a new typelist with elements, the elements of \p List that satisfy the
* invocable \p Pred such that `eval>` is \c true_
*
* Complexity \f$ O(N) \f$
*
* \param List The input typelist
* \param Pred A unary invocable predicate
*/
template
using filter = eval<
filter_impl::filter_>
>;
//! @}
//! replace
//! @{
namespace replace_if_impl {
template
struct replace_if_ { };
template
struct replace_if_, Fn, T, Ret> {
// Recursive call to replace_if_ up to the end of the List, creating a new list
// of items based on invocation of Fn
using type = if_ <
invoke_t,
eval, Fn, T, cat>>>, // re-call with change to T
eval, Fn, T, cat>>> // re-call with no change
>;
};
// At the end return the produced list
template
struct replace_if_ , Fn, T, Ret> {
using type = Ret;
};
}
/*!
* Return a new typelist where all the instances for which the invocation of\p Pred
* returns \c true_, are replaced with \p T
*
* Complexity \f$ O(N) \f$
*
* \param List The input typelist
* \param Pred A unary invocable predicate
* \param T The new type to replace the item of the \p List, when eval>
* returns \c true_
*/
template
using replace_if = eval<
replace_if_impl::replace_if_>
>;
//! Alias wrapper that returns a new \c typelist where all instances of type \p T have
//! been replaced with \p U.
template
using replace = eval <
replace_if , U>
>;
//! @}
//! Returns \c true_ if \p Pred returns \c true_ for all the elements in the \p List or if the
//! \p List is empty and \c false_ otherwise.
template
using all_of = empty<
filter , Pred>>
>;
//! Returns \c true_ if \p Pred returns \c true_ for any of the elements in the \p List
//! and \c false_ otherwise.
template
using any_of = not_<
empty>
>;
//! Returns \c true_ if \p Pred returns \c false_ for all the elements in the \p List
//! or if the \p List is empty and \c false otherwise.
template
using none_of = empty<
filter
>;
}}
//! @}
#endif /* __utl_meta_pack_h__ */