Browse Source

meta: a draft version of typelist and operations for it

doc
Christos Houtouridis 5 years ago
parent
commit
533a09625c
2 changed files with 850 additions and 209 deletions
  1. +631
    -187
      include/utl/meta/typelist.h
  2. +219
    -22
      test/tests/Tmeta.cpp

+ 631
- 187
include/utl/meta/typelist.h View File

@@ -36,7 +36,9 @@ namespace utl {
namespace meta {
/*!
* A class template that just holds a parameter pack.
* \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.
@@ -45,12 +47,15 @@ namespace meta {
* 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).
* ex:
* using l1 = typelist<int, void*, double>;
* \example
* \code
* using l1 = typelist<int, void*, double, void>;
* l1 a {};
* \endcode
*
* boost::hana[3] suggest 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 comse along). So a simple python-like list with some extra vector-like
* responsibility that come 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/
@@ -69,11 +74,43 @@ namespace meta {
static constexpr bool empty() noexcept {
return (sizeof...(Ts) == 0);
}
// ======= times utility =======
private:
template<size_t N, typename L, typename ...T>
struct times_ { };
template<size_t N, typename ...L>
struct times_<N, typelist<L...>, Ts...> {
// append one and recurse
using type = type_<
if_c <N != 0,
times_<N-1, typelist<L..., Ts...>, Ts...>,
typelist<L...>
>>;
};
public:
/*!
* Generate typelist<Ts..., Ts..., ...> of size \c N arguments.
* \example
* \code
* static_assert (
* std::is_same<typelist<int, char>::times<2>,
* typelist<int, char, int, char>
* >, "" );
* \endcode
* complexity \f$ O(N) \f$
*/
template<size_t N>
using times = type_<
times_<N, typelist<>, Ts...>
>;
};
/*!
* 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
*/
@@ -82,7 +119,9 @@ namespace meta {
/*!
* An Boolean constant wrapper that returns if the typelist is empty
*
* Complexity \f$ O(1) \f$.
*
* \param List A typelist
* \return Empty or not
*/
@@ -91,46 +130,205 @@ namespace meta {
//! pair
//! A special typelist with only 2 Types
//! @{
template <typename T1, typename T2>
using pair = typelist<T1, T2>;
//! repeat
//! @{
/*!
* A wrapper to typelist<>::times<> utility for integer argument \p N
*/
template <size_t N, typename ...Ts>
using repeat_c = typename typelist<Ts...>::template times<N>;
/*!
* A wrapper to typelist<>::times<> utility for integral_c argument \p N
*/
template <typename N, typename ...Ts>
using repeat = repeat_c<N::type::value, Ts...>;
//! @}
//! apply
//! like:
//! template <class F, class Tuple>
//! constexpr decltype(auto) apply(F&& f, Tuple&& t);
namespace detail {
template <typename Fn, typename Param>
/*!
* Apply
*/
//! @{
namespace apply_impl {
template <typename Fn, typename Seq>
struct apply_ { };
// apply Param =Ret(Args...)
template <typename Fn, typename Ret, typename... Args>
struct apply_<Fn, Ret(Args...)>
: invoke<Fn, Ret, Args...> { };
// apply Param = F<Args...>
template <typename Fn, template <typename...> class F, typename... Args>
struct apply_<Fn, F<Args...>>
: invoke<Fn, Args...> { };
// apply Param = integer_sequence<T, Is...>
//! \p Sequence == typelist<>
template<typename Fn, typename ...List>
struct apply_<Fn, typelist<List...>> {
using type = invoke<Fn, List...>;
};
//! Sequence == integer_sequence<>
template <typename Fn, typename T, T... Is>
struct apply_<Fn, integer_sequence<T, Is...>>
: invoke<Fn, integral_constant<T, Is>...> { };
struct apply_<Fn, integer_sequence<T, Is...>> {
using type = invoke<Fn, integral_c<T, Is>...>;
};
}
//! Apply the Invocable \p Fn using the types in the type \p Param as arguments.
template <typename Fn, typename Param>
using apply = detail::apply_<Fn, Param>;
/*!
* Apply the Invocable \p Fn using the types in the type \p Seq as arguments.
* \note
* This is the opposed operation of typelist<Ts...>
*
* 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 <typename Fn, typename Seq>
using apply = apply_impl::apply_<Fn, Seq>;
//! @}
/*
* ========= element access ========
*/
//! at: random element access
//! @{
namespace at_impl {
template <typename T> struct _as_pointer__ { using type = T*; };
template <typename T> struct _as_pointer__<T*> { using type = T*; };
template <typename T> using as_pointer_ = type_<
_as_pointer__<T>
>;
template <typename ...>
struct at_head_ { };
template <typename... voids>
struct at_head_ <typelist<voids...>> {
// successful selection N voids, one T* and the rest
template <typename T> static constexpr T select(voids..., T*, ...);
// selection on error
static constexpr nil_ select (...);
};
template<typename List, index_t N>
struct at_ { };
template<typename... List, index_t N>
struct at_<typelist<List...>, N> {
using head_ = at_head_<typelist<void*>::times<N>>; //< make at_head_<> with N void*
using type = decltype(
head_::select(static_cast<as_pointer_<List>>(nullptr)...) //< pass all as List*...
);
};
}
/*!
* Return the \p N th element in the \c meta::typelist \p List.
*
* Complexity \f$ O(N) \f$.
*/
template <typename List, index_t N>
using at_c = type_<
at_impl::at_<List, N>
>;
/*!
* Return the \p N th element in the \c meta::typelist \p List.
*
* Complexity \f$ O(N) \f$.
*/
template <typename List, typename N>
using at = at_c<List, N::type::value>;
//!@}
//! front
//! @{
namespace front_impl {
template <typename L>
struct front_ { };
template <typename Head, typename... Tail>
struct front_<typelist<Head, Tail...>> {
using type = Head;
};
}
//! Return the first element in \c meta::typelist \p List.
//! Complexity \f$ O(1) \f$.
template <typename List>
using front = type_<
front_impl::front_<List>
>;
//! @}
//! back
//! @{
namespace back_impl {
template <typename List>
struct back_ { };
template <typename Head, typename... Tail>
struct back_<typelist<Head, Tail...>> {
using type = at_c <
typelist<Head, Tail...>, sizeof...(Tail)
>;
};
}
//! Return the last element in \c meta::typelist \p List.
//! Complexity \f$ O(N) \f$.
template <typename List>
using back = type_<
back_impl::back_<List>
>;
//! @}
/*
* ========= typelist operations =========
*/
//! Concatenation
//! @{
namespace cat_impl {
template <typename... Lists>
struct cat_ { };
template <>
struct cat_<> {
using type = typelist<>;
};
template <typename... L1>
struct cat_<typelist<L1...>> {
using type = typelist<L1...>;
};
template <typename... L1, typename... L2>
struct cat_<typelist<L1...>, typelist<L2...>> {
using type = typelist<L1..., L2...>;
};
template <typename... L1, typename... L2, typename... Ln>
struct cat_<typelist<L1...>, typelist<L2...>, Ln...>
: cat_ <typelist<L1..., L2...>, 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 <typename... Lists>
using cat = type_<
cat_impl::cat_<Lists...>
>;
//! @}
//! fold<List, V, Fn>, rev_fold<List, V, Fn>
//! @{
namespace detail {
namespace fold_impl {
// fold<<T1, T2, T3>, V, F> == F<F<F<V, T1>, T2>, T3>
template<typename, typename, typename>
struct fold_ { }; // ill formed
@@ -140,19 +338,42 @@ namespace meta {
typename V,
typename Fn>
struct fold_<typelist<Head, Tail...>, V, Fn> {
using type = type_<
fold_<
typelist<Tail...>,
invoke<Fn, V, Head>,
Fn
>
>;
// recursive call of fold_ by consuming typelist and invoking Fn
using type = type_<
fold_<
typelist<Tail...>,
invoke<Fn, V, Head>,
Fn
>
>;
};
// termination call
template<typename V0, typename Fn>
struct fold_<typelist<>, 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<typelist<T1, T2, T3>, V, F> == F<F<F<V, T1>, T2>, T3>
* \example
* fold<typelist<>, V, F> == V
* \param List The list to fold
* \param V The initial item feeded to Fn
* \param Fn The binary Invocable
*/
template <typename List, typename V, typename Fn>
using fold = type_<fold_impl::fold_<List, V, Fn>>;
//! accumulate is an stl name for fold
template <typename List, typename V, typename Fn>
using accumulate = fold<List, V, Fn>;
namespace rev_fold_impl {
// rev_fold<<T1, T2, T3>, V, F> == F<T1, F<T2, F<T3, V>>>
template<typename, typename, typename>
@@ -163,17 +384,18 @@ namespace meta {
typename V,
typename Fn>
struct rev_fold_<typelist<Head, Tail...>, V, Fn> {
using type = invoke <
Fn, Head, type_<
rev_fold_ <
typelist<Tail...>,
V,
Fn
>
>
>;
// recursive call inside invoke. This way the 2nd argument of Fn
// becoming the recursive "thing", inside Fn<>
using type = invoke <
Fn, Head, type_<
rev_fold_ <
typelist<Tail...>,
V,
Fn
>>
>;
};
// termination call
// pre-termination call
template<typename Tail, typename V, typename Fn>
struct rev_fold_ <typelist<Tail>, V, Fn> {
using type = invoke<Fn, Tail, V>;
@@ -181,239 +403,461 @@ namespace meta {
// termination call
template<typename V, typename Fn>
struct rev_fold_ <typelist<>, V, Fn> {
using type = invoke<Fn, V>;
using type = V;
};
}
/*!
* transform the \p List to a new one by doing a left fold using binary Invocable \p Fn
* 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<typelist<T1, T2, T3>, V, F> == F<T1, F<T2, F<T3, V>>>
* \example
* rev_fold<typelist<>, V, F> == V
* \param List The list to fold
* \param V The initial item feeded to Fn
* \param V The initial item fed to Fn
* \param Fn The binary Invocable
*/
template <typename List, typename V, typename Fn>
using fold = type_<detail::fold_<List, V, Fn>>;
//! accumulate is an stl name for fold
template <typename List, typename V, typename Fn>
using accumulate = fold<List, V, Fn>;
using rev_fold = type_<
rev_fold_impl::rev_fold_<List, V, Fn>
>;
//! @}
/*!
* 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$
* \param List The list to fold
* \param V The initial item feeded to Fn
* \param Fn The binary Invocable
* Return a new \c typelist by adding the elements \p Ts to the front of \p List.
* Complexity \f$ O(1) \f$
*/
template <typename List, typename V, typename Fn>
using rev_fold = type_<detail::rev_fold_<List, V, Fn>>;
//! @}
template <typename List, typename... Ts>
using push_front = type_<
apply <
bind_front<quote<typelist>, Ts...>, List
>
>;
//! Concatenation
/*!
* Return a new \c typelist by adding the elements \p Ts to the back of \p List.
* Complexity \f$ O(1) \f$
*/
template <typename List, typename... Ts>
using push_back = type_<
apply <
bind_back<quote<typelist>, Ts...>, List
>
>;
//! reverse
//! @{
namespace detail {
template <typename... Lists>
struct concat_ { };
namespace reverse_impl {
template <>
struct concat_<> {
using type = typelist<>;
template <typename List, typename V = typelist<>>
struct reverse_ {
using type = fold<List, V, quote<push_front>>;
};
}
template <typename... L1>
struct concat_<typelist<L1...>> {
using type = typelist<L1...>;
};
/*!
* Return a new \c typelist by reversing the elements in the list \p List.
* Complexity \f$ O(N) \f$
*/
template <typename List>
using reverse = type_<
reverse_impl::reverse_<List>
>;
//! @}
template <typename... L1, typename... L2>
struct concat_<typelist<L1...>, typelist<L2...>> {
using type = typelist<L1..., L2...>;
};
//! pop_front
//! @{
namespace pop_front_impl {
template <typename List>
struct pop_front_ { };
template <typename... L1, typename... L2, typename... L3>
struct concat_<typelist<L1...>, typelist<L2...>, typelist<L3...>> {
using type = typelist<L1..., L2..., L3...>;
template <typename Head, typename... Tail>
struct pop_front_<typelist <Head, Tail...>> {
using type = typelist<Tail...>;
};
}
template <typename... L1, typename... L2, typename... L3, typename... Rest>
struct concat_<typelist<L1...>, typelist<L2...>, typelist<L3...>, Rest...>
: concat_<typelist<L1..., L2..., L3...>, Rest...> { };
template <typename... L1, typename... L2,
typename... L3, typename... L4,
typename... Rest>
struct concat_<typelist<L1...>, typelist<L2...>, typelist<L3...>, typelist<L4...>, Rest...>
: concat_<typelist<L1..., L2..., L3..., L4...>, Rest...> { };
/*!
* Return a new \c typelist by removing the first element from the
* front of \p List.
* Complexity \f$ O(1) \f$
*/
template <typename List>
using pop_front = type_<
pop_front_impl::pop_front_<List>
>;
//! @}
//! pop_back
//! @{
namespace pop_back_impl {
template <typename List>
struct pop_back_ {
using type = reverse<
pop_front<reverse<List>>
>;
};
}
/*!
* Transformation that concatenates several lists into a single typelist.
* The parameters must all be instantiations of \c meta::typelist.
* Complexity: \f$ O(L) \f$
* where \f$ L \f$ is the number of lists in the typelist of lists.
* 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 <typename... Lists>
using concat = type_<detail::concat_<Lists...>>;
template <typename List>
using pop_back = type_ <
pop_back_impl::pop_back_<List>
>;
//! @}
//! Transform
//! @{
namespace detail {
namespace transform_impl {
template <typename, typename = void>
struct transform_ { };
template <typename... Ts, typename Fn>
struct transform_<typelist<typelist<Ts...>, Fn>, void_<invoke<Fn, Ts>...>> {
using type = typelist<invoke<Fn, Ts>...>;
struct transform_<typelist<typelist<Ts...>, Fn>,
void_t<invoke<Fn, Ts>...> > /* SFINAE check */ {
using type = typelist<
invoke_t<Fn, Ts>...
>;
};
template <typename... Ts0, typename... Ts1, typename Fn>
struct transform_<typelist<typelist<Ts0...>, typelist<Ts1...>, Fn>,
void_<invoke<Fn, Ts0, Ts1>...>> {
using type = typelist<invoke<Fn, Ts0, Ts1>...>;
void_t<invoke<Fn, Ts0, Ts1>...>> /* SFINAE check */ {
using type = typelist<
invoke_t<Fn, Ts0, Ts1>...
>;
};
} // namespace detail
}
/*!
* Transform One or two lists with invocable \c Fn and return a new typelist
* Syntax:
* 1) transform<List..., Fn> Unary invocable
* 2) transform<List1, List2, Fn> Binary invocable
* 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<char, int, ...>;
* using l2 = typelist<void, void, ...>;
* using r1 = transform<l1, F1>; // F1, unary invocable
* using r2 = transform<l1, l2, F2>; // F2, binary invocable
* \endcode
*/
template <typename... Args>
using transform = type_<detail::transform_<typelist<Args...>>>;
using transform = type_<
transform_impl::transform_<typelist<Args...>>
>;
//! @}
//! repeat_n
//! Transform lazy
//! @{
namespace detail {
template <typename T, size_t>
using first_ = T;
namespace transform_lazy_impl {
template <typename, typename = void>
struct transform_lazy_ { };
template <typename T, typename Ints>
struct repeat_n_c_ { };
// Match for Unary Fn with one typelist
template <typename... Ts, typename Fn>
struct transform_lazy_<typelist<typelist<Ts...>, Fn>,
void_t<invoke<Fn, Ts>...> > /* SFINAE check */ {
using type = typelist<
invoke<Fn, Ts>...
>;
};
template <typename T, size_t... Is>
struct repeat_n_c_<T, index_sequence<Is...>> {
using type = typelist<first_<T, Is>...>;
// Match for Binary Fn with two typelists
template <typename... Ts0, typename... Ts1, typename Fn>
struct transform_lazy_<typelist<typelist<Ts0...>, typelist<Ts1...>, Fn>,
void_t<invoke<Fn, Ts0, Ts1>...>> /* SFINAE check */ {
using type = typelist<
invoke<Fn, Ts0, Ts1>...
>;
};
}
/*!
* Generate typelist<T,T,T...T> of size \c N arguments.
* Complexity \f$ O(log N) \f$.
*/
template <index_t N, typename T = void>
using repeat_n_c = type_<detail::repeat_n_c_<T, make_index_sequence<N>>>;
/*!
* Generate typelist<T,T,T...T> of size \c N::type::value arguments.
* Complexity \f$ O(log N) \f$.
* 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<char, int, ...>;
* using l2 = typelist<void, void, ...>;
* using r1 = transform<l1, F1>; // F1, unary invocable
* using r2 = transform<l1, l2, F2>; // F2, binary invocable
* \endcode
*/
template <typename N, typename T = void>
using repeat_n = repeat_n_c<N::type::value, T>;
template <typename... Args>
using transform_lazy = type_<
transform_lazy_impl::transform_lazy_<typelist<Args...>>
>;
//! @}
//! at
//! @{
namespace detail {
template <typename VoidPtrs>
struct at_impl_;
template <typename... VoidPtrs>
struct at_impl_<typelist<VoidPtrs...>> {
static nil_ eval(...);
//! find_if, find
//! @{
namespace find_if_impl {
template <typename, typename, index_t>
struct find_if_ { };
template<typename Head, typename... Tail, typename Fn, index_t N>
struct find_if_<typelist<Head, Tail...>, Fn, N> {
// Recursive call to find_if_ until Fn returns true_
using type = if_ <
invoke_t<Fn, Head>,
index_t_<N>, // done, return current index
type_<find_if_< // not done, re-call find_if_ with the Tail...
typelist<Tail...>, Fn, N+1>
>
>;
};
template <typename T, typename... Us>
static T eval(VoidPtrs..., T *, Us *...);
// When empty or when we are one place after the last item return Npos
template<typename Fn, index_t N>
struct find_if_<typelist<>, Fn, N> {
using type = Npos;
};
}
template <typename L, index_t N>
struct at_ { };
/*!
* Search for the first \c Item on the \p List for which the predicate \p Pred
* returns true_ when `type_<invoke<Pred, Item>>`
*
* 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<typename List, typename Pred>
using find_if = type_<
find_if_impl::find_if_<List, Pred, 0>
>;
template <typename... Ts, index_t N>
struct at_<typelist<Ts...>, N>
: decltype(
at_impl_<repeat_n_c<N, void *>>::eval(static_cast<identity<Ts> *>(nullptr)...)
) { };
} // namespace detail
/*!
* Search for the first occurrence of type \p T on a \p List
*/
template <typename List, typename T>
using find = find_if<List, same_as<T>>;
//! @}
/// Return the \p N th element in the \c meta::typelist \p L.
/// Complexity \f$ O(1) \f$.
template <typename L, index_t N>
using at_c = type_<detail::at_<L, N>>;
//! seek_if
//! @{
namespace seek_if_impl {
template <typename, typename, index_t>
struct seek_if_ { };
template<typename Head, typename... Tail, typename Fn, index_t N>
struct seek_if_<typelist<Head, Tail...>, Fn, N> {
// recursive call to seek_if_ until Fn returns true_
using type = if_ <
invoke_t<Fn, Head>,
typelist<Head, Tail...>, // done, return the typelist starting from here
type_<seek_if_< // not done, re-call seek_if_ with the Tail...
typelist<Tail...>, Fn, N+1>
>
>;
};
//! Return the \p N th element in the \c meta::typelist \p L.
//! Complexity \f$ O(1) \f$.
template <typename L, typename N>
using at = at_c<L, N::type::value>;
//!@}
// When empty or when we are one place after the last item return empty typelist
template<typename Fn, index_t N>
struct seek_if_<typelist<>, Fn, N> {
using type = typelist<>;
};
}
/*!
* Search for the first \c Item on the \p List for which the predicate \p Pred
* returns true_ when `type_<invoke<Pred, Item>>` 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 <typename List, typename Pred>
using seek_if = type_<
seek_if_impl::seek_if_<List, Pred, 0>
>;
/*!
* 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 <typename List, typename T>
using seek = seek_if <List, same_as<T>>;
//! @}
//! front
//! count_if
//! @{
namespace detail {
template <typename L>
struct front_ { };
namespace count_if_impl {
template <typename, typename, size_t>
struct count_if_ { };
template<typename Head, typename... Tail, typename Fn, size_t N>
struct count_if_<typelist<Head, Tail...>, 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<Fn, Head>,
type_<
count_if_<typelist<Tail...>, Fn, N+1> // increase and re-call
>,
type_<
count_if_<typelist<Tail...>, Fn, N> // re-call without increasing
>
>;
};
template <typename Head, typename... Tail>
struct front_<typelist<Head, Tail...>> {
using type = Head;
// At the end of the List return the counter
template<typename Fn, size_t N>
struct count_if_<typelist<>, Fn, N> {
using type = size_t_<N>;
};
}
//! Return the first element in \c meta::typelist \p L.
//! Complexity \f$ O(1) \f$.
template <typename L>
using front = type_<detail::front_<L>>;
/*!
* Count all \c Items on the \p List for which the predicate \p Pred
* returns true_ when `type_<invoke<Pred, Item>>`
*
* 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 <typename List, typename Pred>
using count_if = type_<
count_if_impl::count_if_<List, Pred, 0>
>;
/*!
* Count all occurrences of type \p T int \p List
*/
template <typename List, typename T>
using count = count_if<List, same_as<T>>;
//! @}
//! back
//! filter
//! @{
namespace detail {
template <typename L>
struct back_ { };
namespace filter_impl {
template <typename, typename, typename>
struct filter_ { };
template<typename Head, typename... Tail, typename Fn, typename L>
struct filter_<typelist<Head, Tail...>, 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 <Fn, Head>,
type_<filter_<typelist<Tail...>, Fn, cat<L, typelist<Head>>>>, // Add the element and re-call
type_<filter_<typelist<Tail...>, Fn, L>> // re-call with the same list
>;
};
template <typename Head, typename... Tail>
struct back_<typelist<Head, Tail...>> {
using type = at_c<typelist<Head, Tail...>, sizeof...(Tail)>;
// At the end return the produced list
template<typename Fn, typename L>
struct filter_<typelist<>, Fn, L> {
using type = L;
};
}
//! Return the last element in \c meta::typelist \p L.
//! Complexity \f$ O(1) \f$.
template <typename L>
using back = type_<detail::back_<L>>;
/*!
* Return a new typelist with elements, the elements of \p List that satisfy the
* invocable \p Pred such that `type_<invoke<Pred, Item>>` is \c true_
*
* Complexity \f$ O(N) \f$
*
* \param List The input typelist
* \param Pred A unary invocable predicate
*/
template <typename List, typename Pred>
using filter = type_<
filter_impl::filter_<List, Pred, typelist<>>
>;
//! @}
//! pop_front
//! replace
//! @{
namespace detail {
template <typename L>
struct pop_front_ { };
namespace replace_if_impl {
template <typename, typename, typename, typename>
struct replace_if_ { };
template <typename Head, typename... Tail, typename Fn, typename T, typename Ret>
struct replace_if_<typelist<Head, Tail...>, 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<Fn, Head>,
type_<replace_if_<typelist<Tail...>, Fn, T, cat<Ret, typelist<T>>>>, // re-call with change to T
type_<replace_if_<typelist<Tail...>, Fn, T, cat<Ret, typelist<Head>>>> // re-call with no change
>;
};
template <typename Head, typename... L>
struct pop_front_<typelist<Head, L...>> {
using type = typelist<L...>;
// At the end return the produced list
template <typename Fn, typename T, typename Ret>
struct replace_if_ <typelist<>, Fn, T, Ret> {
using type = Ret;
};
}
/*!
* Return a new \c meta::typelist by removing the first element from the
* front of \p L.
* Complexity \f$ O(1) \f$.
* 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 type_<invoke<Pred, Item>>
* returns \c true_
*/
template <typename L>
using pop_front = type_<detail::pop_front_<L>>;
template<typename List, typename Pred, typename T>
using replace_if = type_<
replace_if_impl::replace_if_<List, Pred, T, typelist<>>
>;
//! Alias wrapper that returns a new \c typelist where all instances of type \p T have
//! been replaced with \p U.
template <typename List, typename T, typename U>
using replace = type_ <
replace_if <List, same_as<T>, 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 <typename List, typename Pred>
using all_of = empty<
filter <List, compose<quote<not_>, Pred>>
>;
//! Returns \c true_ if \p Pred returns \c true_ for any of the elements in the \p List
//! and \c false_ otherwise.
template <typename List, typename Pred>
using any_of = not_<
empty<filter <List, Pred>>
>;
//! 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 <typename List, typename Pred>
using none_of = empty<
filter <List, Pred>
>;
}}
//! @}


+ 219
- 22
test/tests/Tmeta.cpp View File

@@ -26,12 +26,36 @@ namespace test_meta {
using namespace meta;
/*
* Test integral constant
* Types to behave like Fixtures
*/
// Test type_of fixture
template<class T> struct TestTypeOf {
using type = T;
};
template<class T1, class T2> struct MfunBin {
using type = int;
};
template<int a, int b> struct MfunBin_i {
using type = int;
};
template<class T1> struct MfunUn1 {
using type = int;
};
template<class T1> struct MfunUn2 {
using type = int;
};
template <typename T> struct Pred_isInt {
using type = std::is_integral<T>;
};
template <typename T> struct Pred_isVoid {
using type = std::is_void<T>;
};
/*
* Test integral constant
*/
TEST(Tmeta_integral, Integreal_type_) {
EXPECT_EQ(true, (std::is_same<int, type_<TestTypeOf<int>>>::value));
}
@@ -146,16 +170,102 @@ namespace test_meta {
/*
* Test invoke
*/
template<class T1, class T2> struct MetaFunction { using type = int; };
template<typename... Args> struct MetaClass {using apply = int; };
TEST(Tmeta_invoke, Invoke) {
//EXPECT_EQ (true, (is_evaluable<MetaFunction, int .long>()));
using Q = quote<MfunBin>;
using Qi = quote_i<int, MfunBin_i>;
using Q1 = quote<MfunUn1>;
using Q2 = quote<MfunUn2>;
EXPECT_EQ (true, (std::is_same<int, identity_t<int>>()));
EXPECT_EQ (true, (is_applicable_t<MfunBin, int, long>()));
EXPECT_EQ (false, (is_applicable_t<MfunBin, int>()));
EXPECT_EQ (true, (is_applicable_qt<Q, int, long>()));
EXPECT_EQ (false, (is_applicable_qt<Q, int>()));
EXPECT_EQ (true, (is_applicable_it<int, MfunBin_i, 7, 42>()));
EXPECT_EQ (false, (is_applicable_it<int, MfunBin_i, 42>()));
EXPECT_EQ (true, (std::is_same<defer<MfunBin, int, void>::type, MfunBin<int, void>>()));
EXPECT_EQ (true, (std::is_same<defer<MfunBin, void>::type, nil_>()));
EXPECT_EQ (true, (std::is_same<defer_i<int, MfunBin_i, 7, 42>::type, MfunBin_i<7, 42>>()));
EXPECT_EQ (true, (std::is_same<defer_i<int, MfunBin_i, 7>::type, nil_>()));
EXPECT_EQ (true, (std::is_same<invoke<Q, int>, nil_>()));
EXPECT_EQ (true, (std::is_same<invoke<Q, int, void*>, MfunBin<int, void*>>()));
EXPECT_EQ (true, (std::is_same<invoke<Qi, int_<7>, int_<42>>, MfunBin_i<7, 42>>()));
EXPECT_EQ (true, (std::is_same<invoke<Qi, int_<42>>, nil_>()));
EXPECT_EQ (true, (std::is_same<invoke<compose<Q1, Q2>, int>, MfunUn1<MfunUn2<int>>>()));
EXPECT_EQ (true, (std::is_same<
invoke<compose<Q1, Q2, Qi>, int_<7>, int_<42>>,
MfunUn1<MfunUn2<MfunBin_i<7, 42>>>
>()));
EXPECT_EQ (true, (std::is_same<
invoke<compose<Q1, Q2, Qi>, int_<42>>,
MfunUn1<MfunUn2<nil_>>
>()));
EXPECT_EQ (true, (std::is_same<invoke<bind_front<Q, int>, long>, MfunBin<int, long>>()));
EXPECT_EQ (true, (std::is_same<invoke<bind_back<Q, int>, long>, MfunBin<long, int>>()));
}
/*
* Test typelist
*/
template<class T1, class T2> struct F {};
TEST(Tmeta_typelist, List_fold) {
TEST(Tmeta_typelist, Basics) {
using l1 = typelist<int, int, int>;
using l2 = typelist<int, void*, int, void*>;
using l3 = typelist<>;
EXPECT_EQ (true, (std::is_same<l1, typelist<int>::times<3>>()));
EXPECT_EQ (true, (std::is_same<l2, typelist<int, void*>::times<2>>()));
EXPECT_EQ (true, (std::is_same<l3, typelist<>::times<3>>()));
EXPECT_EQ (true, (std::is_same<l3, typelist<int>::times<0>>()));
EXPECT_EQ (true, (std::is_same<typelist<short, double>, pair<short, double>>()));
EXPECT_EQ (true, (std::is_same<l1, repeat <int_<3>, int>>()));
EXPECT_EQ (true, (std::is_same<l2, repeat_c <2, int, void*>>()));
EXPECT_EQ (3, size<l1>());
EXPECT_EQ (true, empty<l3>());
// pass typelist to an invocable
EXPECT_EQ (true, (std::is_same<type_<
apply<quote<MfunBin>, typelist<int, long>>
>,
MfunBin<int, long>
>()));
}
TEST(Tmeta_typelist, Element_access) {
using l = typelist<char, void, long, double, short>;
EXPECT_EQ (true, (std::is_same<char, at_c<l, 0>>()));
EXPECT_EQ (true, (std::is_same<long, at_c<l, 2>>()));
EXPECT_EQ (true, (std::is_same<nil_, at_c<l, 5>>()));
EXPECT_EQ (true, (std::is_same<void, at<l, int_<1>>>()));
EXPECT_EQ (true, (std::is_same<char, front<l>>()));
EXPECT_EQ (true, (std::is_same<short, back<l>>()));
}
TEST(Tmeta_typelist, Concat) {
using l1 = typelist<int, long, void>;
using l2 = typelist<void*, int*>;
using l3 = typelist<double, long double, short>;
using l4 = typelist<>;
using conc = typelist<int, long, void, void*, int*, double, long double, short>;
EXPECT_EQ(true, (std::is_same<l4, cat<l4, l4>>()));
EXPECT_EQ(true, (std::is_same<conc, cat<l1, l2, l3, l4>>()));
EXPECT_EQ(true, (std::is_same<conc, cat<l1, l4, l2, l3>>()));
EXPECT_EQ(true, (std::is_same<conc, cat<l4, l1, l2, l3>>()));
}
template<class T1, class T2> struct F {}; // binary invocable
TEST(Tmeta_typelist, Fold) {
struct X1 {};
struct X2 {};
struct X3 {};
@@ -168,29 +278,116 @@ namespace test_meta {
EXPECT_EQ(true, (std::is_same<fold<typelist<X1, X2, X3>, void, Q>, F<F<F<void, X1>, X2>, X3>>()));
EXPECT_EQ(true, (std::is_same<fold<typelist<X1, X2, X3, X4>, void, Q>, F<F<F<F<void, X1>, X2>, X3>, X4>>()));
//EXPECT_EQ(true, (std::is_same<rev_fold<typelist<>, void, Q>, void>()));
EXPECT_EQ(true, (std::is_same<rev_fold<typelist<>, void, Q>, void>()));
EXPECT_EQ(true, (std::is_same<rev_fold<typelist<X1>, void, Q>, F<X1, void>>()));
EXPECT_EQ(true, (std::is_same<rev_fold<typelist<X1, X2>, void, Q>, F<X1, F<X2, void>>>()));
EXPECT_EQ(true, (std::is_same<rev_fold<typelist<X1, X2, X3>, void, Q>, F<X1, F<X2, F<X3, void>>>>()));
EXPECT_EQ(true, (std::is_same<rev_fold<typelist<X1, X2, X3, X4>, void, Q>, F<X1, F<X2, F<X3, F<X4, void>>>>>()));
}
TEST(Tmeta_typelist, ListOperations) {
using l1 = typelist<int, short, long>;
using l2 = typelist<>;
using l3 = typelist<float, double, long double>;
using l4 = typelist<void*, char*>;
using conc = typelist<int, short, long, float, double, long double, void*, char*>;
using rep = typelist<int, int, int>;
EXPECT_EQ(3, size<l1>());
EXPECT_EQ(true, empty<l2>());
TEST(Tmeta_typelist, PushPopReverse) {
using list = typelist <int, long, void>;
using l_char = typelist <int, long, void, char>;
using l_cc = typelist <int, long, void, char, char>;
using char_l = typelist <char, int, long, void>;
using cc_l = typelist<char, char, int, long, void>;
using rev = typelist<void, long, int>;
EXPECT_EQ (true, (std::is_same<char_l, push_front<list, char>>()));
EXPECT_EQ (true, (std::is_same<cc_l, push_front<list, char, char>>()));
EXPECT_EQ (true, (std::is_same<list, pop_front <char_l>>()));
EXPECT_EQ (true, (std::is_same<l_char, push_back <list, char>>()));
EXPECT_EQ (true, (std::is_same<l_cc, push_back <list, char, char>>()));
EXPECT_EQ (true, (std::is_same<list, pop_back <l_char>>()));
EXPECT_EQ (true, (std::is_same<rev, reverse <list>>()));
}
TEST(Tmeta_typelist, Transform) {
using QBin = quote<MfunBin>;
using QUn = quote<MfunUn1>;
using l1 = typelist<char, int, float>;
using l2 = typelist<void, void, void>;
using r = typelist<int, int, int>;
using r_ulazy = typelist <MfunUn1<char>, MfunUn1<int>, MfunUn1<float>>;
using r_blazy = typelist <MfunBin<char, void>, MfunBin<int, void>, MfunBin<float, void>>;
EXPECT_EQ (true, (std::is_same<r, transform<l1, QUn>>()));
EXPECT_EQ (true, (std::is_same<r, transform<l1, l2, QBin>>()));
EXPECT_EQ (true, (std::is_same<r_ulazy, transform_lazy<l1, QUn>>()));
EXPECT_EQ (true, (std::is_same<r_blazy, transform_lazy<l1, l2, QBin>>()));
}
TEST(Tmeta_typelist, Find) {
using l1 = typelist <int, char, long, float>;
using l2 = typelist <char, long, float>;
using l3 = typelist <long, float>;
using empty = typelist<>;
EXPECT_EQ(true, (std::is_same<index_t_<1>, find_if<l1, same_as<char>>>()));
EXPECT_EQ(true, (std::is_same<Npos, find_if<empty, same_as<char>>>()));
EXPECT_EQ(true, (std::is_same<Npos, find_if<l1, same_as<double>>>()));
EXPECT_EQ(true, (std::is_same<index_t_<2>, find<l1, long>>()));
EXPECT_EQ(true, (std::is_same<l2, seek_if<l1, same_as<char>>>()));
EXPECT_EQ(true, (std::is_same<empty, seek_if<empty, same_as<char>>>()));
EXPECT_EQ(true, (std::is_same<empty, seek_if<l1, same_as<double>>>()));
EXPECT_EQ(true, (std::is_same<l3, seek<l1, long>>()));
}
TEST(Tmeta_typelist, Count) {
using list = typelist<int, void*, char, int, long*, char, int, short>;
using empty = typelist<>;
EXPECT_EQ (true, (std::is_same<size_t_<3>, count_if<list, same_as<int>>>()));
EXPECT_EQ (true, (std::is_same<size_t_<2>, count_if<list, same_as<char>>>()));
EXPECT_EQ (true, (std::is_same<size_t_<0>, count_if<list, same_as<double>>>()));
EXPECT_EQ (true, (std::is_same<size_t_<0>, count_if<empty, int>>()));
EXPECT_EQ (true, (std::is_same<size_t_<1>, count<list, void*>>()));
}
TEST(Tmeta_typelist, Filter) {
using Q1 = quote<Pred_isInt>;
using Q2 = quote<Pred_isVoid>;
using list = typelist<int, float, char, long*, short, double, void*>;
using filtered = typelist<int, char, short>;
EXPECT_EQ (true, (std::is_same<filtered, filter<list, Q1>>()));
EXPECT_EQ (true, (std::is_same<typelist<>, filter<typelist<>, Q1>>()));
EXPECT_EQ (true, (std::is_same<typelist<>, filter<list, Q2>>()));
}
TEST(Tmeta_typelist, Replace) {
using Q = quote<Pred_isInt>;
using list = typelist<int, float, char, long*, short, double, void*>;
using res = typelist<void,float, void, long*, void, double, void*>;
using repl = typelist<int, float, void, long*, short, double, void*>;
EXPECT_EQ (true, (std::is_same<res, replace_if<list, Q, void>>()));
EXPECT_EQ (true, (std::is_same<typelist<>, replace_if<typelist<>, Q, void>>()));
EXPECT_EQ (true, (std::is_same<res, replace_if<res, Q, void>>()));
EXPECT_EQ (true, (std::is_same<repl, replace<list, char, void>>()));
}
TEST (Tmeta_typelist, AllAnyNone) {
using l1 = typelist<int, float, char, long*, short, double, void*>;
using l2 = typelist<int, char, long, short>;
EXPECT_EQ(true, (std::is_same<conc, concat<l1, l2, l3, l4>>()));
EXPECT_EQ(true, (std::is_same<rep, repeat_n<int_<3>, int>>()));
EXPECT_EQ(true, (std::is_same<int, front<l1>>()));
EXPECT_EQ(true, (std::is_same<long, back<l1>>()));
EXPECT_EQ(true, (std::is_same<double, at<l3, int_<1>>>()));
EXPECT_EQ (true, (std::is_same<false_, all_of<l1, quote<Pred_isInt>>>()));
EXPECT_EQ (true, (std::is_same<true_, all_of<l2, quote<Pred_isInt>>>()));
EXPECT_EQ(true, (std::is_same<typelist<short, long>, pop_front<l1>>()));
EXPECT_EQ (true, (std::is_same<true_, any_of<l1, quote<Pred_isInt>>>()));
EXPECT_EQ (true, (std::is_same<false_, any_of<l2, quote<Pred_isVoid>>>()));
EXPECT_EQ (true, (std::is_same<true_, none_of<l1, quote<Pred_isVoid>>>()));
EXPECT_EQ (true, (std::is_same<false_, none_of<l1, quote<Pred_isInt>>>()));
}
}

Loading…
Cancel
Save