/*! * \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__ */