1
0
forked from boostorg/mp11

Merge branch 'develop'

This commit is contained in:
Peter Dimov
2017-06-10 03:46:37 +03:00
13 changed files with 1277 additions and 304 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -7,11 +7,13 @@ See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt
////
# Boost.Mp11
# Boost.Mp11: A C++11 metaprogramming library
Peter Dimov
:toc: left
:toclevels: 3
:idprefix:
:listing-caption: Code Example
:table-caption: Illustration
:leveloffset: +1

View File

@@ -19,36 +19,141 @@ http://www.boost.org/LICENSE_1_0.txt
`mp_assign<L1<T1...>, L2<T2...>>` is an alias for `L1<T2...>`. That is, it replaces the elements of `L1` with those of `L2`.
.Using mp_assign with mp_list and std::tuple
```
using L1 = std::tuple<long>;
using L2 = mp_list<int, float>;
using R1 = mp_assign<L1, L2>; // std::tuple<int, float>
```
.Using mp_assign with mp_list and std::pair
```
using L1 = std::pair<long, char>;
using L2 = mp_list<int, float>;
using R1 = mp_assign<L1, L2>; // std::pair<int, float>
```
## mp_clear<L>
template<class L> using mp_clear = mp_assign<L, mp_list<>>;
`mp_clear<L<T...>>` is an alias for `L<>`, that is, it removes the elements of `L`.
.Using mp_clear with std::tuple
```
using L1 = std::tuple<int, float>;
using R1 = mp_clear<L1>; // std::tuple<>
```
## mp_transform<F, L...>
template<template<class...> class F, class... L> using mp_transform = /*...*/;
`mp_transform<F, L1<T1...>, L2<T2...>, ..., Ln<Tn...>>` applies `F` to each successive tuple of elements and returns `L1<F<T1, T2, ..., Tn>...>`.
.Using mp_transform to produce a list of pointers from a list of pointees
```
template<class T> using add_pointer_t =
typename std::add_pointer<T>::type; // std::add_pointer_t in C++14
using L1 = std::tuple<void, int, float>;
using R1 = mp_transform<add_pointer_t, L1>; // std::tuple<void*, int*, float*>
```
.Using mp_transform to compare the contents of two lists of types
```
using L1 = std::tuple<void, int, float>;
using L2 = mp_list<void, int, float>;
using R1 = mp_all<mp_transform<std::is_same, L1, L2>>; // mp_true
```
.Using mp_transform to compare the contents of two lists of integral constants
```
template<class T1, class T2> using eq = mp_bool<T1::value == T2::value>;
using L1 = std::tuple<mp_int<1>, mp_int<2>, mp_int<3>>;
using L2 = mp_list<mp_size_t<1>, mp_size_t<2>, mp_size_t<3>>;
using R1 = mp_all<mp_transform<eq, L1, L2>>; // mp_true
```
.mp_transform on one list
[cols="<.^4m,4*^.^1m",options="header",width=85%]
|===
^|List
4+|Contents
5+|
|*L1*|A0|A1|...|An
5+|
|*mp_transform<F, L1>*|F<A0>|F<A1>|...|F<An>
|===
.mp_transform on two lists
[cols="<.^4m,4*^.^1m",options="header",width=85%]
|===
^|List
4+|Contents
5+|
|*L1*|A0|A1|...|An
5+|
|*L2*|B0|B1|...|Bn
5+|
|*mp_transform<F, L1, L2>*|F<A0,B0>|F<A1,B1>|...|F<An,Bn>
|===
## mp_transform_q<Q, L...>
template<class Q, class... L> using mp_transform_q = mp_transform<Q::template fn, L...>;
template<class Q, class... L> using mp_transform_q =
mp_transform<Q::template fn, L...>;
As `mp_transform`, but takes a quoted metafunction.
.Using mp_transform_q to count the occurences of `void` in a list
```
using L1 = std::tuple<void, int, float, void, int>;
using R1 = mp_apply<mp_plus,
mp_transform_q<mp_bind_front<std::is_same, void>, L1>>; // mp_int\<2>
```
## mp_transform_if<P, F, L...>
template<template<class...> class P, template<class...> class F, class L...> using mp_transform_if = /*...*/;
template<template<class...> class P, template<class...> class F, class L...>
using mp_transform_if = /*...*/;
`mp_transform_if<P, F, L1, L2, ..., Ln>` replaces the elements of the list `L1` for which `mp_to_bool<P<T1, T2, ..., Tn>>` is `mp_true` with
`F<T1, T2, ..., Tn>`, and returns the result, where `Ti` are the corresponding elements of `Li`.
.Using mp_transform_if to replace the occurences of 'void' in a list with the corresponding elements of a second list
```
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;
template<class T1, class T2> using first_is_void = std::is_same<T1, void>;
template<class T1, class T2> using second = T2;
using R1 = mp_transform_if<first_is_void, second, L1, L2>;
// std::tuple<char[1], int, float, char[4], int>
```
## mp_transform_if_q<Qp, Qf, L...>
template<class Qp, class Qf, class... L> using mp_transform_if_q = mp_transform_if<Qp::template fn, Qf::template fn, L...>;
template<class Qp, class Qf, class... L> using mp_transform_if_q =
mp_transform_if<Qp::template fn, Qf::template fn, L...>;
As `mp_transform_if`, but takes a quoted metafunction.
As `mp_transform_if`, but takes quoted metafunctions.
.Using mp_transform_if_q to replace the occurences of 'void' in a list with the corresponding elements of a second list
```
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;
using R1 = mp_transform_if_q<mp_bind<std::is_same, _1, void>, _2, L1, L2>;
// std::tuple<char[1], int, float, char[4], int>
```
## mp_fill<L, V>
@@ -56,6 +161,18 @@ As `mp_transform_if`, but takes a quoted metafunction.
`mp_fill<L<T...>, V>` returns `L<V, V, ..., V>`, with the result having the same size as the input.
.Using mp_fill with std::tuple
```
using L1 = std::tuple<void, int, float>;
using R1 = mp_fill<L1, double>; // std::tuple<double, double, double>
```
.Using mp_fill with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_fill<L1, void>; // std::pair<void, void>
```
## mp_count<L, V>
template<class L, class V> using mp_count = /*...*/;
@@ -94,6 +211,32 @@ Same as `mp_repeat_c` but with a type argument `N`. The number of copies is `N::
the Cartesian product of the lists, as if the elements `Ui` are formed by `n` nested loops, each traversing `Li`.
It returns a list of type `L1` containing the results of the application of `F`.
.mp_product on two lists
[cols="<.^4m,4*^.^1m",options="header",width=85%]
|===
^|List
4+|Contents
5+|
|*L1*|A0|A1|...|An
5+|
|*L2*|B0|B1|...|Bm
5+|
|*mp_product<F, L1, L2>*
|F<A0,B0>
F<A0,B1>
...
F<A0,Bm>
|F<A1,B0>
F<A1,B1>
...
F<A1,Bm>
|...
|F<An,B0>
F<An,B1>
...
F<An,Bm>
|===
## mp_product_q<Q, L...>
template<class Q, class... L> using mp_product_q = mp_product<Q::template fn, L...>;
@@ -130,7 +273,7 @@ where `T` is the type of `N::value`.
template<class L, std::size_t I> using mp_at_c = /*...*/;
`mp_at_c<L, I>` returns the `I`th element of `L`, zero-based.
`mp_at_c<L, I>` returns the `I`-th element of `L`, zero-based.
## mp_at<L, I>
@@ -152,25 +295,29 @@ Same as `mp_take_c`, but with a type argument `N`. `N::value` must be a nonnegat
## mp_insert_c<L, I, T...>
template<class L, std::size_t I, class... T> using mp_insert_c = mp_append<mp_take_c<L, I>, mp_push_front<mp_drop_c<L, I>, T...>>;
template<class L, std::size_t I, class... T> using mp_insert_c =
mp_append<mp_take_c<L, I>, mp_push_front<mp_drop_c<L, I>, T...>>;
Inserts the elements `T...` into the list `L` at position `I` (a zero-based index).
## mp_insert<L, I, T...>
template<class L, class I, class... T> using mp_insert = mp_append<mp_take<L, I>, mp_push_front<mp_drop<L, I>, T...>>;
template<class L, class I, class... T> using mp_insert =
mp_append<mp_take<L, I>, mp_push_front<mp_drop<L, I>, T...>>;
Same as `mp_insert_c`, but with a type argument `I`.
## mp_erase_c<L, I, J>
template<class L, std::size_t I, std::size_t J> using mp_erase = mp_append<mp_take_c<L, I>, mp_drop_c<L, J>>;
template<class L, std::size_t I, std::size_t J> using mp_erase_c =
mp_append<mp_take_c<L, I>, mp_drop_c<L, J>>;
Removes from the list `L` the elements with indices from `I` (inclusive) to `J` (exclusive).
## mp_erase<L, I, J>
template<class L, class I, class J> using mp_erase = mp_append<mp_take<L, I>, mp_drop<L, J>>;
template<class L, class I, class J> using mp_erase =
mp_append<mp_take<L, I>, mp_drop<L, J>>;
Same as `mp_erase_c`, but with a type arguments `I` and `J`.
@@ -229,6 +376,14 @@ for the elements of `L<U1...>` and `mp_false` for the elements of `L<U2...>`. Re
`mp_sort<L, P>` sorts the list `L` according to the strict weak ordering `mp_to_bool<P<T, U>>`.
.Using mp_sort to sort a list of std::ratio values
----
#include <ratio>
using L1 = mp_list<std::ratio<1,2>, std::ratio<1,4>>;
using R1 = mp_sort<L1, std::ratio_less>; // mp_list<ratio<1,4>, ratio<1,2>>
----
## mp_find<L, V>
template<class L, class V> using mp_find = /*...*/;
@@ -258,7 +413,8 @@ is `mp_size<L>`.
## mp_reverse_fold<L, V, F>
template<class L, class V, template<class...> class F> using mp_reverse_fold = /*...*/;
template<class L, class V, template<class...> class F> using mp_reverse_fold =
/*...*/;
`mp_reverse_fold<L<T1, T2, ..., Tn>, V, F>` is `F<T1, F<T2, F<..., F<Tn, V>>>>`, or `V`, if `L` is empty.
@@ -270,19 +426,22 @@ is `mp_size<L>`.
## mp_all_of<L, P>
template<class L, template<class...> class P> using mp_all_of = mp_bool< mp_count_if<L, P>::value == mp_size<L>::value >;
template<class L, template<class...> class P> using mp_all_of =
mp_bool< mp_count_if<L, P>::value == mp_size<L>::value >;
`mp_all_of<L, P>` is `mp_true` when `P` holds for all elements of `L`, `mp_false` otherwise. When `L` is empty, the result is `mp_true`.
## mp_none_of<L, P>
template<class L, template<class...> class P> using mp_none_of = mp_bool< mp_count_if<L, P>::value == 0 >;
template<class L, template<class...> class P> using mp_none_of =
mp_bool< mp_count_if<L, P>::value == 0 >;
`mp_none_of<L, P>` is `mp_true` when `P` holds for no element of `L`, `mp_false` otherwise. When `L` is empty, the result is `mp_true`.
## mp_any_of<L, P>
template<class L, template<class...> class P> using mp_any_of = mp_bool< mp_count_if<L, P>::value != 0 >;
template<class L, template<class...> class P> using mp_any_of =
mp_bool< mp_count_if<L, P>::value != 0 >;
`mp_any_of<L, P>` is `mp_true` when `P` holds for at least one element of `L`, `mp_false` otherwise. When `L` is empty, the result is `mp_false`.
@@ -294,12 +453,47 @@ is `mp_size<L>`.
Returns `std::forward<F>(f)`.
.Using mp_for_each and a C++14 lambda to print a tuple
```
template<class... T> void print( std::tuple<T...> const & tp )
{
std::size_t const N = sizeof...(T);
mp_for_each<mp_iota_c<N>>( []( auto I ){
// I is mp_size_t<0>, mp_size_t<1>, ..., mp_size_t<N-1>
std::cout << std::get<I>(tp) << std::endl;
});
}
```
## mp_with_index<N>(i, f)
template<std::size_t N, class F> decltype(std::declval<F>()(std::declval<mp_size_t<0>>())) mp_with_index( std::size_t i, F && f );
template<std::size_t N, class F>
constexpr auto mp_with_index( std::size_t i, F && f )
-> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));
`mp_with_index<N>(i, f)` calls `f` with `mp_size_t<i>()` and returns the result. `i` must be less than `N`.
Only `constexpr` on C++14 and higher.
template<class N, class F> decltype(std::declval<F>()(std::declval<mp_size_t<0>>())) mp_with_index( std::size_t i, F && f );
template<class N, class F>
constexpr auto mp_with_index( std::size_t i, F && f )
-> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));
Returns `mp_with_index<N::value>(i, f)`.
.Using mp_with_index and a C++14 lambda to print the active element of a variant
```
template<class... T> void print( std::variant<T...> const& v )
{
mp_with_index<sizeof...(T)>( v.index(), [&]( auto I ) {
// I is mp_size_t<v.index()> here
std::cout << std::get<I>( v ) << std::endl;
});
}
```

View File

@@ -57,7 +57,8 @@ As `mp_bind`, but takes a quoted metafunction.
## mp_bind_front_q<Q, T...>
template<class Q, class... T> using mp_bind_front_q = mp_bind_front<Q::template fn, T...>;
template<class Q, class... T> using mp_bind_front_q =
mp_bind_front<Q::template fn, T...>;
As `mp_bind_front`, but takes a quoted metafunction.
@@ -69,6 +70,7 @@ As `mp_bind_front`, but takes a quoted metafunction.
## mp_bind_back_q<Q, T...>
template<class Q, class... T> using mp_bind_back_q = mp_bind_back<Q::template fn, T...>;
template<class Q, class... T> using mp_bind_back_q =
mp_bind_back<Q::template fn, T...>;
As `mp_bind_back`, but takes a quoted metafunction.

View File

@@ -16,15 +16,24 @@ for example `mp_list<char[], void>`, `mp_list<>`, `std::tuple<int, float, char>`
A _metafunction_ is a class template or a template alias whose parameters are all types, for example `std::add_pointer_t`,
`std::is_const`, `mp_second`, `mp_push_front`, `mp_list`, `std::tuple`, `std::pair`, `std::shared_ptr`, or
```
template<class...> using F1 = void;
template<class T> using F2 = T*;
template<class... T> using F3 = std::integral_constant<std::size_t, sizeof...(T)>;
```
A _quoted metafunction_ is a class with a public metafunction member called `fn`, for example
```
struct Q1 { template<class...> using fn = void; };
struct Q2 { template<class T> using fn = T*; };
struct Q3 { template<class... T> using fn = std::integral_constant<std::size_t, sizeof...(T)>; };
struct Q3 { template<class... T> using fn =
std::integral_constant<std::size_t, sizeof...(T)>; };
```
An _integral constant type_ is a class with a public member `value` that is an integral constant in the C++ sense. For example,
`std::integral_constant<int, 7>`, or
@@ -35,5 +44,10 @@ A _set_ is a list whose elements are unique.
A _map_ is a list of lists, the inner lists having at least one element (the key.) The keys of the map must be unique. For example,
using M1 = std::tuple<std::pair<int, int*>, std::pair<float, float*>, std::pair<void, void*>>;
using M2 = mp_list<mp_list<int, int*>, mp_list<float>, mp_list<char, char[1], char[2]>>;
```
using M1 = std::tuple<std::pair<int, int*>, std::pair<float, float*>,
std::pair<void, void*>>;
using M2 = mp_list<mp_list<int, int*>, mp_list<float>,
mp_list<char, char[1], char[2]>>;
```

View File

@@ -118,7 +118,7 @@ int main()
tuple_for_each( mp_product<mp_list, L, L>(), test_result() );
}
```
## Writing `common_type` specializations
## Writing common_type Specializations
The standard trait `std::common_type`, used to obtain a type to which all of its arguments can convert without
unnecessary loss of precision, can be user-specialized when its default implementation (based on the ternary `?:`
@@ -163,7 +163,8 @@ combined into their common type, and we take the union of the set of error types
Therefore,
```
template<class T1, class E1, class T2, class E2> using common_expected =
mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, common_type_t<T1, T2>>, expected>;
mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, common_type_t<T1, T2>>,
expected>;
namespace std
{
@@ -181,10 +182,11 @@ the `E` types. This makes our job easier. `mp_unique<mp_append<E1, E2>>` gives u
removed; we then add `common_type_t<T1, T2>` to the front via `mp_push_front`; and finally, we `mp_rename` the resultant `mp_list`
to `expected`.
## Fixing `tuple_cat`
## Fixing tuple_cat
The article http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html#[Simple C++11 metaprogramming] builds an
implementation of the standard function `tuple_cat`, with the end result given below:
```
template<class L> using F = mp_iota<mp_size<L>>;
@@ -202,7 +204,9 @@ template<class... Tp,
// inner
using list1 = mp_list<mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;
using list1 = mp_list<
mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;
using list2 = mp_iota_c<N>;
using list3 = mp_transform<mp_fill, list1, list2>;
@@ -217,11 +221,14 @@ template<class... Tp,
//
return tuple_cat_<R>( inner(), outer(), std::forward_as_tuple( std::forward<Tp>(tp)... ) );
return tuple_cat_<R>( inner(), outer(),
std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}
```
This function, however, is not entirely correct, in that it doesn't handle some cases properly. For example,
trying to concatenate tuples containing move-only elements such as `unique_ptr` fails:
```
std::tuple<std::unique_ptr<int>> t1;
std::tuple<std::unique_ptr<float>> t2;
@@ -247,7 +254,7 @@ auto result = ::tuple_cat( t1, t2 );
Let's fix these one by one. Support for move-only types is easy, if one knows where to look. The problem is
that `Tp` that we're passing to the helper `tuple_cat_` is (correctly) `tuple<unique_ptr<int>&&, unique_ptr<float>&&>`,
but `std::get<0>(tp)` still returns `unique_ptr<int>&`, because `tp` is an lvalue. This behavior is a bit
surprising, but consistent with how rvalue reference members are treated by the language.
surprising, but is intended to prevent inadvertent double moves.
Long story short, we need `std::move(tp)` in `tuple_cat_` to make `tp` an rvalue:
@@ -266,6 +273,7 @@ primitive that is inexplicably missing from the standard library:
typename std::remove_reference<T>::type>::type;
and then by using `remove_cv_ref<Tp>` in place of `typename std::remove_reference<Tp>::type`:
```
template<class... Tp,
class R = mp_append<std::tuple<>, remove_cv_ref<Tp>...>>
@@ -279,6 +287,7 @@ template<class... Tp,
// ...
```
Finally, tuple-like types. We've so far exploited the fact that `std::pair` and `std::tuple` are valid Mp11 lists,
but in general, arbitrary tuple-like types aren't, so we need to convert them into such. For that, we'll need to
define a metafunction `from_tuple_like` that will take an arbitrary tuple-like type and will return, in our case,
@@ -289,6 +298,7 @@ convenient.
What we need is, given a tuple-like type `Tp`, to obtain `mp_list<std::tuple_element<0, Tp>::type, std::tuple_element<1, Tp>::type,
..., std::tuple_element<N-1, Tp>::type>`, where `N` is `tuple_size<Tp>::value`. Here's one way to do it:
```
template<class T, class I> using tuple_element =
typename std::tuple_element<I::value, T>::type;
@@ -296,6 +306,7 @@ template<class T, class I> using tuple_element =
template<class T> using from_tuple_like =
mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
```
(`mp_iota<N>` is an algorithm that returns an `mp_list` with elements `mp_size_t<0>`, `mp_size_t<1>`, ..., `mp_size_t<N-1>`.)
Remember that `mp_product<F, L1, L2>` performs the equivalent of two nested loops over the elements of `L1` and `L2`,
@@ -310,6 +321,7 @@ For completeness's sake, here's another, more traditional way to achieve the sam
mp_transform_q<mp_bind_front<tuple_element, T>, mp_iota<std::tuple_size<T>>>;
With all these fixes applied, our fully operational `tuple_cat` now looks like this:
```
template<class L> using F = mp_iota<mp_size<L>>;
@@ -351,9 +363,11 @@ template<class... Tp,
//
return tuple_cat_<R>( inner(), outer(), std::forward_as_tuple( std::forward<Tp>(tp)... ) );
return tuple_cat_<R>( inner(), outer(),
std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}
```
## Computing Return Types
C++17 has a standard variant type, called `std::variant`. It also defines a function template
@@ -372,6 +386,7 @@ what `v1` and `v2` hold.
A type that can hold either `int` or `float` already exists, called, surprisingly enough, `std::variant<int, float>`.
Let's write our own function template `rvisit` that is the same as `visit` but returns a `variant`:
```
template<class F, class... V> auto rvisit( F&& f, V&&... v )
{
@@ -382,6 +397,7 @@ template<class F, class... V> auto rvisit( F&& f, V&&... v )
std::forward<V>( v )... );
}
```
What this does is basically calls `std::visit` to do the work, but instead of passing it `f`, we pass a lambda that does the same as `f` except
it converts the result to a common type `R`. `R` is supposed to be `std::variant<...>` where the ellipsis denotes the return types of
calling `f` with all possible combinations of variant values.
@@ -390,7 +406,8 @@ We'll first define a helper quoted metafunction `Qret<F>` that returns the resul
template<class F> struct Qret
{
template<class... T> using fn = decltype( std::declval<F>()( std::declval<T>()... ) );
template<class... T> using fn =
decltype( std::declval<F>()( std::declval<T>()... ) );
};
(Unfortunately, we can't just define this metafunction inside `rvisit`; the language prohibits defining template aliases inside functions.)
@@ -405,11 +422,12 @@ the same as `mp_product`, but for quoted metafunctions such as our `Qret<F>`.)
One more step remains. Suppose that, as above, we're passing two variants of type `std::variant<short, int, float>` and `F` is
`[]( auto const& x, auto const& y ){ return x + y; }`. This will generate `R` of length 9, one per each combination, but many of those
elements will be the same, either `int` or `float`, and we need to filter out the duplicates. So,
elements will be the same, either `int` or `float`, and we need to filter out the duplicates. So, we pass the result to `mp_unique`:
using R = mp_unique<mp_product_q<Qret<F>, std::remove_reference_t<V>...>>;
and we're done:
```
#include <boost/mp11.hpp>
#include <boost/core/demangle.hpp>
@@ -422,7 +440,8 @@ using namespace boost::mp11;
template<class F> struct Qret
{
template<class... T> using fn = decltype( std::declval<F>()( std::declval<T>()... ) );
template<class... T> using fn =
decltype( std::declval<F>()( std::declval<T>()... ) );
};
template<class F, class... V> auto rvisit( F&& f, V&&... v )
@@ -439,25 +458,26 @@ template<class T> std::string name()
return boost::core::demangle( typeid(T).name() );
}
template<class V> void print_variant( char const * n, V const& v )
{
std::cout << "(" << name<decltype(v)>() << ")" << n << ": ";
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v );
}
int main()
{
std::variant<signed char, unsigned char, signed short, unsigned short,
int, unsigned> v1( 1 );
std::variant<char, int, float> v1( 1 );
std::cout << "(" << name<decltype(v1)>() << ")v1: ";
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v1 );
print_variant( "v1", v1 );
std::variant<int, float, double> v2( 2.0f );
std::variant<short, int, double> v2( 3.14 );
std::cout << "(" << name<decltype(v2)>() << ")v2: ";
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v2 );
print_variant( "v2", v2 );
auto v3 = rvisit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );
std::cout << "(" << name<decltype(v3)>() << ")v3: ";
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v3 );
print_variant( "v3", v3 );
}
```

View File

@@ -27,10 +27,16 @@ Same as `std::void_t` from C++17.
returns `mp_false`. If the application causes a substitution failure, returns `mp_false`. If all results are `mp_true`,
returns `mp_true`. `mp_and<>` is `mp_true`.
.mp_and behavior
```
using R1 = mp_and<mp_true, mp_true>; // mp_true
using R2 = mp_and<mp_false, void>; // mp_false, void is not reached
using R3 = mp_and<mp_false, mp_false>; // mp_false
using R4 = mp_and<void, mp_true>; // mp_false (!)
```
## mp_all<T...>
@@ -41,10 +47,16 @@ returns `mp_true`. `mp_and<>` is `mp_true`.
is an error because `void` does not have a nested `value`. The upside is that `mp_all` is potentially faster and does not
mask substitution failures as `mp_and` does.
using R1 = mp_and<mp_true, mp_true>; // mp_true
using R2 = mp_and<mp_false, void>; // compile-time error
using R3 = mp_and<mp_false, mp_false>; // mp_false
using R4 = mp_and<void, mp_true>; // compile-time error
.mp_all behavior
```
using R1 = mp_all<mp_true, mp_true>; // mp_true
using R2 = mp_all<mp_false, void>; // compile-time error
using R3 = mp_all<mp_false, mp_false>; // mp_false
using R4 = mp_all<void, mp_true>; // compile-time error
```
## mp_or<T...>
@@ -53,10 +65,16 @@ mask substitution failures as `mp_and` does.
`mp_or<T...>` applies `mp_to_bool` to the types in `T...`, in order. If the result of an application is `mp_true`, `mp_or`
returns `mp_true`. If all results are `mp_false`, returns `mp_false`. `mp_or<>` is `mp_false`.
.mp_or behavior
```
using R1 = mp_or<mp_true, mp_false>; // mp_true
using R2 = mp_or<mp_true, void>; // mp_true, void is not reached
using R3 = mp_or<mp_false, mp_false>; // mp_false
using R4 = mp_or<void, mp_true>; // compile-time error
```
## mp_any<T...>
@@ -65,10 +83,16 @@ returns `mp_true`. If all results are `mp_false`, returns `mp_false`. `mp_or<>`
`mp_any<T...>` is `mp_true` if `mp_to_bool<U>` is `mp_true` for any type `U` in `T...`, `mp_false` otherwise. Same as
`mp_or`, but does not perform short-circuit evaluation.
.mp_any behavior
```
using R1 = mp_any<mp_true, mp_false>; // mp_true
using R2 = mp_any<mp_true, void>; // compile-time error
using R3 = mp_any<mp_false, mp_false>; // mp_false
using R4 = mp_any<void, mp_true>; // compile-time error
```
## mp_same<T...>

View File

@@ -35,12 +35,14 @@ http://www.boost.org/LICENSE_1_0.txt
## make_index_sequence<N>
template<std::size_t N> using make_index_sequence = make_integer_sequence<std::size_t, N>;
template<std::size_t N> using make_index_sequence =
make_integer_sequence<std::size_t, N>;
`make_index_sequence<N>` is `index_sequence<0, 1, ..., N-1>`. Same as C++14's `std::make_index_sequence`.
## index_sequence_for<T...>
template<class... T> using index_sequence_for = make_integer_sequence<std::size_t, sizeof...(T)>;
template<class... T> using index_sequence_for =
make_integer_sequence<std::size_t, sizeof...(T)>;
`index_sequence_for<N>` is `make_index_sequence<sizeof...(T)>`. Same as C++14's `std::index_sequence_for`.

View File

@@ -19,14 +19,20 @@ For an Mp11 integral constant type `T`, `T::value` is an integral constant in th
template<bool B> using mp_bool = std::integral_constant<bool, B>;
Same as `std::bool_constant` in C++17.
## mp_true
using mp_true = mp_bool<true>;
Same as `std::true_type`.
## mp_false
using mp_false = mp_bool<false>;
Same as `std::false_type`.
## mp_to_bool<T>
template<class T> using mp_to_bool = mp_bool<static_cast<bool>(T::value)>;

View File

@@ -28,14 +28,23 @@ the list.
`mp_size<L>` returns the number of elements in the list `L`, as a `mp_size_t`. In other words, `mp_size<L<T...>>` is an alias for
`mp_size_t<sizeof...(T)>`.
.Using mp_size with mp_list
```
using L1 = mp_list<>;
using R1 = mp_size<L1>; // mp_size_t\<0>
```
.Using mp_size with std::pair
```
using L2 = std::pair<int, int>;
using R2 = mp_size<L2>; // mp_size_t\<2>
```
.Using mp_size with std::tuple
```
using L3 = std::tuple<float>;
using R3 = mp_size<L3>; // mp_size_t\<1>
```
## mp_empty<L>
@@ -43,20 +52,38 @@ the list.
`mp_empty<L>` is an alias for `mp_true` if the list `L` is empty, for `mp_false` otherwise.
.Using mp_empty with std::tuple
```
using L1 = std::tuple<float>;
using R1 = mp_empty<L1>; // mp_false
using L2 = std::tuple<>;
using R2 = mp_empty<L2>; // mp_true
```
## mp_front<L>
template<class L> using mp_front = /*...*/;
`mp_front<L>` is the first element of the list `L`. That is, `mp_front<L<T1, T...>>` is an alias for `T1`.
.Using mp_front with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_front<L1>; // int
```
.Using mp_front with std::tuple
```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_front<L2>; // float
```
.Using mp_front with mp_list
```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_front<L3>; // char[1]
```
## mp_pop_front<L>
@@ -64,11 +91,17 @@ the list.
`mp_pop_front<L>` removes the first element of the list `L`. That is, `mp_pop_front<L<T1, T...>>` is an alias for `L<T...>`.
.Using mp_pop_front with std::tuple
```
using L1 = std::tuple<float, double, long double>;
using R1 = mp_pop_front<L1>; // std::tuple<double, long double>
```
.Using mp_pop_front with mp_list
```
using L2 = mp_list<void>;
using R2 = mp_pop_front<L2>; // mp_list<>
```
## mp_first<L>
@@ -88,14 +121,23 @@ the list.
`mp_second<L>` is the second element of the list `L`. That is, `mp_second<L<T1, T2, T...>>` is an alias for `T2`.
.Using mp_second with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_second<L1>; // float
```
.Using mp_second with std::tuple
```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_second<L2>; // double
```
.Using mp_second with mp_list
```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_second<L3>; // char[2]
```
## mp_third<L>
@@ -103,11 +145,17 @@ the list.
`mp_third<L>` is the third element of the list `L`. That is, `mp_third<L<T1, T2, T3, T...>>` is an alias for `T3`.
.Using mp_third with std::tuple
```
using L1 = std::tuple<float, double, long double>;
using R1 = mp_third<L1>; // long double
```
.Using mp_third with mp_list
```
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_third<L2>; // char[3]
```
## mp_push_front<L, T...>
@@ -116,11 +164,17 @@ the list.
`mp_push_front<L, T...>` inserts the elements `T...` at the front of the list `L`. That is, `mp_push_front<L<U...>, T...>`
is an alias for `L<T..., U...>`.
.Using mp_push_front with std::tuple
```
using L1 = std::tuple<double, long double>;
using R1 = mp_push_front<L1, float>; // std::tuple<float, double, long double>
```
.Using mp_push_front with mp_list
```
using L2 = mp_list<void>;
using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void>
```
## mp_push_back<L, T...>
@@ -129,11 +183,17 @@ is an alias for `L<T..., U...>`.
`mp_push_back<L, T...>` inserts the elements `T...` at the back of the list `L`. That is, `mp_push_back<L<U...>, T...>`
is an alias for `L<U..., T...>`.
.Using mp_push_back with std::tuple
```
using L1 = std::tuple<double, long double>;
using R1 = mp_push_back<L1, float>; // std::tuple<double, long double, float>
```
.Using mp_push_back with mp_list
```
using L2 = mp_list<void>;
using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]>
```
## mp_rename<L, Y>
@@ -141,11 +201,17 @@ is an alias for `L<U..., T...>`.
`mp_rename<L, Y>` changes the type of the list `L` to `Y`. That is, `mp_rename<L<T...>, Y>` is an alias for `Y<T...>`.
.Using mp_rename to rename std::pair to std::tuple
```
using L1 = std::pair<double, long double>;
using R1 = mp_rename<L1, std::tuple>; // std::tuple<double, long double>
```
.Using mp_rename to rename std::tuple to mp_list
```
using L2 = std::tuple<void>;
using R2 = mp_rename<L2, mp_list>; // mp_list<void>
```
## mp_apply<F, L>
@@ -154,14 +220,19 @@ is an alias for `L<U..., T...>`.
`mp_apply<F, L>` applies the metafunction `F` to the contents of the list `L`, that is, `mp_apply<F, L<T...>>` is an alias for `F<T...>`.
(`mp_apply` is the same as `mp_rename` with the arguments reversed.)
.Using mp_apply with std::pair
```
using L1 = std::pair<double, long double>;
using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double>
```
## mp_apply_q<Q, L>
template<class Q, class L> using mp_apply_q = mp_apply<Q::template fn, L>;
Same as `mp_apply`, but takes a quoted metafunction.
.Using mp_apply_q with mp_bind_front
```
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int, long>;
@@ -169,20 +240,25 @@ using L2 = mp_list<int, long>;
using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>;
// R1 is std::tuple<double, long double, int, long>
```
## mp_append<L...>
template<class... L> using mp_append = /*...*/;
`mp_append<L...>` concatenates the lists in `L...` into a single list that has the same type as the first list. `mp_append<>`
is an alias for `mp_list<>`. `mp_append<L1<T1...>, L2<T2...>, ..., Ln<Tn...>>` is an alias for `L1<T1..., T2..., ..., Tn...>`.
.Using mp_append with lists of various types
```
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int>;
using L3 = std::pair<short, long>;
using L4 = mp_list<>;
using R1 = mp_append<L1, L2, L3, L4>; // std::tuple<double, long double, int, short, long>
using R1 = mp_append<L1, L2, L3, L4>;
// std::tuple<double, long double, int, short, long>
```
## mp_replace_front<L, T>
template<class L, class T> using mp_replace_front = /*...*/;
@@ -190,14 +266,23 @@ using R1 = mp_append<L1, L2, L3, L4>; // std::tuple<double, long double, int, sh
`mp_replace_front<L, T>` replaces the first element of the list `L` with `T`. That is, `mp_replace_front<L<U1, U...>, T>` is
an alias for `L<T, U...>`.
.Using mp_replace_front with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_replace_front<L1, void>; // std::pair<void, float>
```
.Using mp_replace_front with std::tuple
```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double>
```
.Using mp_replace_front with mp_list
```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_front<L3, void>; // mp_list<void, char[2], char[3], char[4]>;
```
## mp_replace_first<L, T>
@@ -212,14 +297,23 @@ an alias for `L<T, U...>`.
`mp_replace_second<L, T>` replaces the second element of the list `L` with `T`. That is, `mp_replace_second<L<U1, U2, U...>, T>`
is an alias for `L<U1, T, U...>`.
.Using mp_replace_second with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_replace_second<L1, void>; // std::pair<int, void>
```
.Using mp_replace_second with std::tuple
```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double>
```
.Using mp_replace_front with mp_list
```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_second<L3, void>; // mp_list<char[1], void, char[3], char[4]>;
```
## mp_replace_third<L, T>
@@ -228,8 +322,14 @@ is an alias for `L<U1, T, U...>`.
`mp_replace_third<L, T>` replaces the third element of the list `L` with `T`. That is, `mp_replace_third<L<U1, U2, U3, U...>, T>`
is an alias for `L<U1, U2, T, U...>`.
.Using mp_replace_third with std::tuple
```
using L1 = std::tuple<float, double, long double>;
using R1 = mp_replace_third<L1, void>; // std::tuple<float, double, void>
```
.Using mp_replace_third with mp_list
```
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_replace_third<L2, void>; // mp_list<char[1], char[2], void, char[4]>;
```

View File

@@ -23,13 +23,15 @@ A map is a list of lists, the inner lists having at least one element (the key.)
## mp_map_contains<M, K>
template<class M, class K> using mp_map_contains = mp_not<std::is_same<mp_map_find<M, K>, void>>;
template<class M, class K> using mp_map_contains =
mp_not<std::is_same<mp_map_find<M, K>, void>>;
`mp_map_contains<M, K>` is `mp_true` if the map `M` contains an element with a key `K`, `mp_false` otherwise.
## mp_map_insert<M, T>
template<class M, class T> using mp_map_insert = mp_if< mp_map_contains<M, mp_first<T>>, M, mp_push_back<M, T> >;
template<class M, class T> using mp_map_insert =
mp_if< mp_map_contains<M, mp_first<T>>, M, mp_push_back<M, T> >;
Inserts the element `T` into the map `M`, if an element with a key `mp_first<T>` is not already in `M`.

View File

@@ -10,8 +10,9 @@ http://www.boost.org/LICENSE_1_0.txt
[#overview]
# Overview
Mp11 is a C++11 metaprogramming library based on template aliases and variadic templates.
It implements the approach outlined in the article
Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures
that contain types. It's based on template aliases and variadic templates and implements the
approach outlined in the article
http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html["Simple {cpp} metaprogramming"]
and http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html[its sequel]. Reading these
articles before proceeding with this documentation is _highly_ recommended.

View File

@@ -20,6 +20,35 @@ http://www.boost.org/LICENSE_1_0.txt
using type = T;
};
`mp_identity` is a simple _transformation type trait_ (as per the C++ standard)
that just returns the same type. It's useful both as such, and as a type wrapper
useful for passing types as values to functions.
.Using mp_identity as a type trait
```
template<class T> using addp_if_not_ref =
typename mp_if<std::is_reference<T>, mp_identity<T>, std::add_pointer<T>>::type;
```
.Using mp_identity to protect qualifiers and references
```
template<class T> void print1()
{
std::cout << typeid(T).name() << std::endl;
}
template<class T> void print2()
{
std::cout << typeid(mp_identity<T>).name() << std::endl;
}
int main()
{
print1<int const&>(); // 'int'
print2<int const&>(); // 'mp_identity<int const &>'
}
```
## mp_identity_t<T>
template<class T> using mp_identity_t = T;
@@ -34,33 +63,75 @@ http://www.boost.org/LICENSE_1_0.txt
`mp_if_c<true, T, E...>` is an alias for `T`. `mp_if_c<false, T, E>` is an alias for `E`. Otherwise, the result is a substitution failure.
.Using mp_if_c to select between two alternatives
```
using R1 = mp_if_c<true, int, void>; // int
using R2 = mp_if_c<flase, int, void>; // void
template<class I> using void_if_5 = mp_if_c<I::value == 5, void>; // `void` when `I::value` is 5, substitution failure otherwise
using R2 = mp_if_c<false, int, void>; // void
```
.Using mp_if_c to fail substitution when a condition is not met
```
template<class I> using void_if_5 = mp_if_c<I::value == 5, void>;
```
This example returns `void` when `I::value` is 5, and generates a substitution failure
otherwise. It's the same as `std::enable_if_t<I::value == 5>` in {cpp}14, or
`typename std::enable_if<I::value == 5>::type` in {cpp}11.
## mp_if<C, T, E...>
template<class C, class T, class E...> using mp_if = mp_if_c<static_cast<bool>(C::value), T, E...>;
template<class C, class T, class E...> using mp_if =
mp_if_c<static_cast<bool>(C::value), T, E...>;
Like `mp_if_c`, but the first argument is a type.
.Using mp_if to select between two alternatives
```
using R1 = mp_if<mp_true, int, void>; // int
using R2 = mp_if<mp_false, int, void>; // void
```
.Using mp_if to fail substitution when a condition is not met
```
template<class T> using void_if_const = mp_if<std::is_const<T>, void>;
template<class... T> using void_if_all_const =
mp_if<mp_all<std::is_const<T>...>, void>;
template<class T> using if_non_const = mp_if<mp_not<std::is_const<T>>, T>;
```
## mp_eval_if_c<C, T, F, U...>
template<bool C, class T, template<class...> class F, class... U> using mp_eval_if_c = /*...*/;
template<bool C, class T, template<class...> class F, class... U> using mp_eval_if_c =
/*...*/;
`mp_eval_if_c<C, T, F, U...>` is an alias for `T` when `C` is `true`, for `F<U...>` otherwise. Its purpose
is to avoid evaluating `F<U...>` when the condition is `true` as it may not be valid in this case.
.Using mp_eval_if_c to select the first pack element, or void
```
template<class... T> using first_or_void =
mp_eval_if_c<sizeof...(T) == 0, void, mp_apply, mp_first, mp_list<T...>>;
```
## mp_eval_if<C, T, F, U...>
template<class C, class T, template<class...> class F, class... U> using mp_eval_if = mp_eval_if_c<static_cast<bool>(C::value), T, F, U...>;
template<class C, class T, template<class...> class F, class... U> using mp_eval_if =
mp_eval_if_c<static_cast<bool>(C::value), T, F, U...>;
Like `mp_eval_if_c`, but the first argument is a type.
.Using mp_eval_if to select the first list element, or void
```
template<class L> using first_or_void = mp_eval_if<mp_empty<L>, void, mp_first, L>;
```
## mp_eval_if_q<C, T, Q, U...>
template<class C, class T, class Q, class... U> using mp_eval_if_q = mp_eval_if<C, T, Q::template fn, U...>;
template<class C, class T, class Q, class... U> using mp_eval_if_q =
mp_eval_if<C, T, Q::template fn, U...>;
Like `mp_eval_if`, but takes a quoted metafunction.
@@ -70,6 +141,13 @@ Like `mp_eval_if`, but takes a quoted metafunction.
`mp_valid<F, T...>` is an alias for `mp_true` when `F<T...>` is a valid expression, for `mp_false` otherwise.
.Using mp_valid to write a metafunction that checks for the existence of a nested type
```
template<class T> using get_nested_type = typename T::type;
template<class T> struct has_nested_type: mp_valid<get_nested_type, T> {};
```
## mp_defer<F, T...>
template<template<class...> class F, class... T> using mp_defer = /*...*/;
@@ -86,8 +164,33 @@ When `mp_valid<F, T...>` is `mp_true`, `mp_defer<F, T...>` is a struct with a ne
`mp_quote<F>` transforms the template `F` into a type with a nested template `fn` such that `fn<T...>` returns `F<T...>`.
.Using mp_quote to make a list of metafunctions
```
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;
```
## mp_invoke<Q, T...>
template<class Q, class... T> using mp_invoke = typename Q::template fn<T...>;
`mp_invoke<Q, T...>` evaluates the nested template `fn` of a quoted metafunction. `mp_invoke<mp_quote<F>, T...>` returns `F<T...>`.
.Using mp_invoke to invoke a list of metafunctions, technique 1
```
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;
template<class T> using is_const_and_volatile =
mp_all<mp_product<mp_invoke, LQ, mp_list<T>>>;
```
.Using mp_invoke to invoke a list of metafunctions, technique 2
```
template<class T> using is_const_and_volatile =
mp_all<mp_transform_q<mp_bind_back<mp_invoke, T>, LQ>>;
```
.Using mp_invoke to invoke a list of metafunctions, technique 3
```
template<class T> using is_const_and_volatile =
mp_all<mp_transform<mp_invoke, LQ, mp_fill<LQ, T>>>;
```