1
0
forked from boostorg/mp11

Update documentation

This commit is contained in:
Peter Dimov
2017-06-10 03:44:31 +03:00
parent 9deb752095
commit cbf539e16b
13 changed files with 1216 additions and 449 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 http://www.boost.org/LICENSE_1_0.txt
//// ////
# Boost.Mp11 # Boost.Mp11: A C++11 metaprogramming library
Peter Dimov Peter Dimov
:toc: left :toc: left
:toclevels: 3 :toclevels: 3
:idprefix: :idprefix:
:listing-caption: Code Example
:table-caption: Illustration
:leveloffset: +1 :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`. `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> ## mp_clear<L>
template<class L> using mp_clear = mp_assign<L, mp_list<>>; 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`. `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...> ## mp_transform<F, L...>
template<template<class...> class F, class... L> using mp_transform = /*...*/; 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>...>`. `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...> ## 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. 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...> ## 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 `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`. `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...> ## 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> ## 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. `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> ## mp_count<L, V>
template<class L, class V> using mp_count = /*...*/; 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`. 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`. 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...> ## mp_product_q<Q, L...>
template<class Q, class... L> using mp_product_q = mp_product<Q::template fn, 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 = /*...*/; 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> ## mp_at<L, I>
@@ -233,13 +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>>`. `mp_sort<L, P>` sorts the list `L` according to the strict weak ordering `mp_to_bool<P<T, U>>`.
Example: .Using mp_sort to sort a list of std::ratio values
``` ----
#include <ratio> #include <ratio>
using L1 = mp_list<std::ratio<1,2>, std::ratio<1,4>>; 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>> using R1 = mp_sort<L1, std::ratio_less>; // mp_list<ratio<1,4>, ratio<1,2>>
``` ----
## mp_find<L, V> ## mp_find<L, V>
template<class L, class V> using mp_find = /*...*/; template<class L, class V> using mp_find = /*...*/;
@@ -269,7 +413,8 @@ is `mp_size<L>`.
## mp_reverse_fold<L, V, F> ## 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. `mp_reverse_fold<L<T1, T2, ..., Tn>, V, F>` is `F<T1, F<T2, F<..., F<Tn, V>>>>`, or `V`, if `L` is empty.
@@ -308,12 +453,47 @@ is `mp_size<L>`.
Returns `std::forward<F>(f)`. 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) ## 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`. `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)`. 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...> ## 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. 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...> ## 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. 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`, 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 `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...> using F1 = void;
template<class... T> using F3 = std::integral_constant<std::size_t, sizeof...(T)>;
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 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 Q1 { template<class...> using fn = void; };
struct Q3 { template<class... T> using fn = std::integral_constant<std::size_t, sizeof...(T)>; };
struct Q2 { template<class T> using fn = 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, 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 `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, 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() ); 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 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 `?:` 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, Therefore,
``` ```
template<class T1, class E1, class T2, class E2> using common_expected = 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 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` 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`. to `expected`.
## Fixing `tuple_cat` ## Fixing tuple_cat
The article http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html#[Simple C++11 metaprogramming] builds an 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: implementation of the standard function `tuple_cat`, with the end result given below:
``` ```
template<class L> using F = mp_iota<mp_size<L>>; template<class L> using F = mp_iota<mp_size<L>>;
@@ -202,7 +204,9 @@ template<class... Tp,
// inner // 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 list2 = mp_iota_c<N>;
using list3 = mp_transform<mp_fill, list1, list2>; 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, 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: 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<int>> t1;
std::tuple<std::unique_ptr<float>> t2; 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 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>&&>`, 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 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: 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; typename std::remove_reference<T>::type>::type;
and then by using `remove_cv_ref<Tp>` in place of `typename std::remove_reference<Tp>::type`: and then by using `remove_cv_ref<Tp>` in place of `typename std::remove_reference<Tp>::type`:
``` ```
template<class... Tp, template<class... Tp,
class R = mp_append<std::tuple<>, remove_cv_ref<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, 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 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, 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, 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: ..., 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 = template<class T, class I> using tuple_element =
typename std::tuple_element<I::value, T>::type; 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 = template<class T> using from_tuple_like =
mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>; 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>`.) (`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`, 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>>>; 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: With all these fixes applied, our fully operational `tuple_cat` now looks like this:
``` ```
template<class L> using F = mp_iota<mp_size<L>>; 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 ## Computing Return Types
C++17 has a standard variant type, called `std::variant`. It also defines a function template 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>`. 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`: 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 ) 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 )... ); 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 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 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. 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 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.) (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 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 `[]( 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>...>>; using R = mp_unique<mp_product_q<Qret<F>, std::remove_reference_t<V>...>>;
and we're done: and we're done:
``` ```
#include <boost/mp11.hpp> #include <boost/mp11.hpp>
#include <boost/core/demangle.hpp> #include <boost/core/demangle.hpp>
@@ -422,7 +440,8 @@ using namespace boost::mp11;
template<class F> struct Qret 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 ) 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() ); 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() int main()
{ {
std::variant<signed char, unsigned char, signed short, unsigned short, std::variant<char, int, float> v1( 1 );
int, unsigned> v1( 1 );
std::cout << "(" << name<decltype(v1)>() << ")v1: "; print_variant( "v1", v1 );
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v1 );
std::variant<int, float, double> v2( 2.0f ); std::variant<short, int, double> v2( 3.14 );
std::cout << "(" << name<decltype(v2)>() << ")v2: "; print_variant( "v2", v2 );
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v2 );
auto v3 = rvisit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 ); auto v3 = rvisit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );
std::cout << "(" << name<decltype(v3)>() << ")v3: "; print_variant( "v3", v3 );
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v3 );
} }
``` ```

View File

@@ -27,15 +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_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`. returns `mp_true`. `mp_and<>` is `mp_true`.
Examples: .mp_and behavior
```
using R1 = mp_and<mp_true, mp_true>; // mp_true
using R1 = mp_and<mp_true, mp_true>; // mp_true using R2 = mp_and<mp_false, void>; // mp_false, void is not reached
using R2 = mp_and<mp_false, void>; // mp_false, void is not reached using R3 = mp_and<mp_false, mp_false>; // mp_false
using R3 = mp_and<mp_false, mp_false>; // mp_false using R4 = mp_and<void, mp_true>; // mp_false (!)
```
using R4 = mp_and<void, mp_true>; // mp_false (!)
## mp_all<T...> ## mp_all<T...>
@@ -46,15 +47,16 @@ Examples:
is an error because `void` does not have a nested `value`. The upside is that `mp_all` is potentially faster and does not 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. mask substitution failures as `mp_and` does.
Examples: .mp_all behavior
```
using R1 = mp_all<mp_true, mp_true>; // mp_true
using R1 = mp_all<mp_true, mp_true>; // mp_true using R2 = mp_all<mp_false, void>; // compile-time error
using R2 = mp_all<mp_false, void>; // compile-time error using R3 = mp_all<mp_false, mp_false>; // mp_false
using R3 = mp_all<mp_false, mp_false>; // mp_false using R4 = mp_all<void, mp_true>; // compile-time error
```
using R4 = mp_all<void, mp_true>; // compile-time error
## mp_or<T...> ## mp_or<T...>
@@ -63,15 +65,16 @@ Examples:
`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` `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`. returns `mp_true`. If all results are `mp_false`, returns `mp_false`. `mp_or<>` is `mp_false`.
Examples: .mp_or behavior
```
using R1 = mp_or<mp_true, mp_false>; // mp_true
using R1 = mp_or<mp_true, mp_false>; // mp_true using R2 = mp_or<mp_true, void>; // mp_true, void is not reached
using R2 = mp_or<mp_true, void>; // mp_true, void is not reached using R3 = mp_or<mp_false, mp_false>; // mp_false
using R3 = mp_or<mp_false, mp_false>; // mp_false using R4 = mp_or<void, mp_true>; // compile-time error
```
using R4 = mp_or<void, mp_true>; // compile-time error
## mp_any<T...> ## mp_any<T...>
@@ -80,15 +83,16 @@ Examples:
`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_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_or`, but does not perform short-circuit evaluation.
Examples: .mp_any behavior
```
using R1 = mp_any<mp_true, mp_false>; // mp_true
using R1 = mp_any<mp_true, mp_false>; // mp_true using R2 = mp_any<mp_true, void>; // compile-time error
using R2 = mp_any<mp_true, void>; // compile-time error using R3 = mp_any<mp_false, mp_false>; // mp_false
using R3 = mp_any<mp_false, mp_false>; // mp_false using R4 = mp_any<void, mp_true>; // compile-time error
```
using R4 = mp_any<void, mp_true>; // compile-time error
## mp_same<T...> ## mp_same<T...>

View File

@@ -35,12 +35,14 @@ http://www.boost.org/LICENSE_1_0.txt
## make_index_sequence<N> ## 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`. `make_index_sequence<N>` is `index_sequence<0, 1, ..., N-1>`. Same as C++14's `std::make_index_sequence`.
## index_sequence_for<T...> ## 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`. `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>; template<bool B> using mp_bool = std::integral_constant<bool, B>;
Same as `std::bool_constant` in C++17.
## mp_true ## mp_true
using mp_true = mp_bool<true>; using mp_true = mp_bool<true>;
Same as `std::true_type`.
## mp_false ## mp_false
using mp_false = mp_bool<false>; using mp_false = mp_bool<false>;
Same as `std::false_type`.
## mp_to_bool<T> ## mp_to_bool<T>
template<class T> using mp_to_bool = mp_bool<static_cast<bool>(T::value)>; template<class T> using mp_to_bool = mp_bool<static_cast<bool>(T::value)>;

View File

@@ -28,16 +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<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)>`. `mp_size_t<sizeof...(T)>`.
Examples: .Using mp_size with mp_list
```
using L1 = mp_list<>;
using R1 = mp_size<L1>; // mp_size_t\<0>
```
using L1 = mp_list<>; .Using mp_size with std::pair
using R1 = mp_size<L1>; // mp_size_t\<0> ```
using L2 = std::pair<int, int>;
using R2 = mp_size<L2>; // mp_size_t\<2>
```
using L2 = std::pair<int, int>; .Using mp_size with std::tuple
using R2 = mp_size<L2>; // mp_size_t\<2> ```
using L3 = std::tuple<float>;
using L3 = std::tuple<float>; using R3 = mp_size<L3>; // mp_size_t\<1>
using R3 = mp_size<L3>; // mp_size_t\<1> ```
## mp_empty<L> ## mp_empty<L>
@@ -45,22 +52,38 @@ Examples:
`mp_empty<L>` is an alias for `mp_true` if the list `L` is empty, for `mp_false` otherwise. `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> ## mp_front<L>
template<class L> using mp_front = /*...*/; 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`. `mp_front<L>` is the first element of the list `L`. That is, `mp_front<L<T1, T...>>` is an alias for `T1`.
Examples: .Using mp_front with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_front<L1>; // int
```
using L1 = std::pair<int, float>; .Using mp_front with std::tuple
using R1 = mp_front<L1>; // int ```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_front<L2>; // float
```
using L2 = std::tuple<float, double, long double>; .Using mp_front with mp_list
using R2 = mp_front<L2>; // float ```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using L3 = mp_list<char[1], char[2], char[3], char[4]>; using R3 = mp_front<L3>; // char[1]
using R3 = mp_front<L3>; // char[1] ```
## mp_pop_front<L> ## mp_pop_front<L>
@@ -68,13 +91,17 @@ Examples:
`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...>`. `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...>`.
Examples: .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 L1 = std::tuple<float, double, long double>; .Using mp_pop_front with mp_list
using R1 = mp_pop_front<L1>; // std::tuple<double, long double> ```
using L2 = mp_list<void>;
using L2 = mp_list<void>; using R2 = mp_pop_front<L2>; // mp_list<>
using R2 = mp_pop_front<L2>; // mp_list<> ```
## mp_first<L> ## mp_first<L>
@@ -94,16 +121,23 @@ Examples:
`mp_second<L>` is the second element of the list `L`. That is, `mp_second<L<T1, T2, T...>>` is an alias for `T2`. `mp_second<L>` is the second element of the list `L`. That is, `mp_second<L<T1, T2, T...>>` is an alias for `T2`.
Examples: .Using mp_second with std::pair
```
using L1 = std::pair<int, float>;
using R1 = mp_second<L1>; // float
```
using L1 = std::pair<int, float>; .Using mp_second with std::tuple
using R1 = mp_second<L1>; // float ```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_second<L2>; // double
```
using L2 = std::tuple<float, double, long double>; .Using mp_second with mp_list
using R2 = mp_second<L2>; // double ```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using L3 = mp_list<char[1], char[2], char[3], char[4]>; using R3 = mp_second<L3>; // char[2]
using R3 = mp_second<L3>; // char[2] ```
## mp_third<L> ## mp_third<L>
@@ -111,13 +145,17 @@ Examples:
`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`. `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`.
Examples: .Using mp_third with std::tuple
```
using L1 = std::tuple<float, double, long double>;
using R1 = mp_third<L1>; // long double
```
using L1 = std::tuple<float, double, long double>; .Using mp_third with mp_list
using R1 = mp_third<L1>; // long double ```
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using L2 = mp_list<char[1], char[2], char[3], char[4]>; using R2 = mp_third<L2>; // char[3]
using R2 = mp_third<L2>; // char[3] ```
## mp_push_front<L, T...> ## mp_push_front<L, T...>
@@ -126,13 +164,17 @@ Examples:
`mp_push_front<L, T...>` inserts the elements `T...` at the front of the list `L`. That is, `mp_push_front<L<U...>, T...>` `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...>`. is an alias for `L<T..., U...>`.
Examples: .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 L1 = std::tuple<double, long double>; .Using mp_push_front with mp_list
using R1 = mp_push_front<L1, float>; // std::tuple<float, double, long double> ```
using L2 = mp_list<void>;
using L2 = mp_list<void>; using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void>
using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void> ```
## mp_push_back<L, T...> ## mp_push_back<L, T...>
@@ -141,13 +183,17 @@ Examples:
`mp_push_back<L, T...>` inserts the elements `T...` at the back of the list `L`. That is, `mp_push_back<L<U...>, T...>` `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...>`. is an alias for `L<U..., T...>`.
Examples: .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 L1 = std::tuple<double, long double>; .Using mp_push_back with mp_list
using R1 = mp_push_back<L1, float>; // std::tuple<double, long double, float> ```
using L2 = mp_list<void>;
using L2 = mp_list<void>; using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]>
using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]> ```
## mp_rename<L, Y> ## mp_rename<L, Y>
@@ -155,13 +201,17 @@ Examples:
`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...>`. `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...>`.
Examples: .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 L1 = std::pair<double, long double>; .Using mp_rename to rename std::tuple to mp_list
using R1 = mp_rename<L1, std::tuple>; // std::tuple<double, long double> ```
using L2 = std::tuple<void>;
using L2 = std::tuple<void>; using R2 = mp_rename<L2, mp_list>; // mp_list<void>
using R2 = mp_rename<L2, mp_list>; // mp_list<void> ```
## mp_apply<F, L> ## mp_apply<F, L>
@@ -170,10 +220,11 @@ Examples:
`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<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.) (`mp_apply` is the same as `mp_rename` with the arguments reversed.)
Example: .Using mp_apply with std::pair
```
using L1 = std::pair<double, long double>; using L1 = std::pair<double, long double>;
using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double> using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double>
```
## mp_apply_q<Q, L> ## mp_apply_q<Q, L>
@@ -181,14 +232,13 @@ Example:
Same as `mp_apply`, but takes a quoted metafunction. Same as `mp_apply`, but takes a quoted metafunction.
Example: .Using mp_apply_q with mp_bind_front
``` ```
using L1 = std::tuple<double, long double>; using L1 = std::tuple<double, long double>;
using L2 = mp_list<int, long>; using L2 = mp_list<int, long>;
using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>; using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>;
// R1 is std::tuple<double, long double, int, long> // R1 is std::tuple<double, long double, int, long>
``` ```
## mp_append<L...> ## mp_append<L...>
@@ -198,15 +248,15 @@ using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>;
`mp_append<L...>` concatenates the lists in `L...` into a single list that has the same type as the first list. `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...>`. is an alias for `mp_list<>`. `mp_append<L1<T1...>, L2<T2...>, ..., Ln<Tn...>>` is an alias for `L1<T1..., T2..., ..., Tn...>`.
Example: .Using mp_append with lists of various types
``` ```
using L1 = std::tuple<double, long double>; using L1 = std::tuple<double, long double>;
using L2 = mp_list<int>; using L2 = mp_list<int>;
using L3 = std::pair<short, long>; using L3 = std::pair<short, long>;
using L4 = mp_list<>; 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> ## mp_replace_front<L, T>
@@ -216,16 +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 `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...>`. an alias for `L<T, U...>`.
Examples: .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 L1 = std::pair<int, float>; .Using mp_replace_front with std::tuple
using R1 = mp_replace_front<L1, void>; // std::pair<void, float> ```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double>
```
using L2 = std::tuple<float, double, long double>; .Using mp_replace_front with mp_list
using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double> ```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
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]>;
using R3 = mp_replace_front<L3, void>; // mp_list<void, char[2], char[3], char[4]>; ```
## mp_replace_first<L, T> ## mp_replace_first<L, T>
@@ -240,16 +297,23 @@ Examples:
`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>` `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...>`. is an alias for `L<U1, T, U...>`.
Examples: .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 L1 = std::pair<int, float>; .Using mp_replace_second with std::tuple
using R1 = mp_replace_second<L1, void>; // std::pair<int, void> ```
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double>
```
using L2 = std::tuple<float, double, long double>; .Using mp_replace_front with mp_list
using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double> ```
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
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]>;
using R3 = mp_replace_second<L3, void>; // mp_list<char[1], void, char[3], char[4]>; ```
## mp_replace_third<L, T> ## mp_replace_third<L, T>
@@ -258,10 +322,14 @@ Examples:
`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>` `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...>`. is an alias for `L<U1, U2, T, U...>`.
Examples: .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 L1 = std::tuple<float, double, long double>; .Using mp_replace_third with mp_list
using R1 = mp_replace_third<L1, void>; // std::tuple<float, double, void> ```
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
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]>;
using R2 = mp_replace_third<L2, void>; // mp_list<char[1], char[2], void, char[4]>; ```

View File

@@ -23,7 +23,8 @@ A map is a list of lists, the inner lists having at least one element (the key.)
## mp_map_contains<M, K> ## 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_contains<M, K>` is `mp_true` if the map `M` contains an element with a key `K`, `mp_false` otherwise.

View File

@@ -10,8 +10,9 @@ http://www.boost.org/LICENSE_1_0.txt
[#overview] [#overview]
# Overview # Overview
Mp11 is a C++11 metaprogramming library based on template aliases and variadic templates. Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures
It implements the approach outlined in the article 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"] 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 and http://pdimov.com/cpp2/simple_cxx11_metaprogramming_2.html[its sequel]. Reading these
articles before proceeding with this documentation is _highly_ recommended. 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; 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> ## mp_identity_t<T>
template<class T> using mp_identity_t = T; template<class T> using mp_identity_t = T;
@@ -34,14 +63,20 @@ 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. `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.
Examples: .Using mp_if_c to select between two alternatives
```
using R1 = mp_if_c<true, int, void>; // int
using R1 = mp_if_c<true, int, void>; // int using R2 = mp_if_c<false, int, void>; // void
```
using R2 = mp_if_c<flase, 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>; template<class I> using void_if_5 = mp_if_c<I::value == 5, void>;
// `void` when `I::value` is 5, substitution failure otherwise ```
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...> ## mp_if<C, T, E...>
@@ -50,13 +85,37 @@ Examples:
Like `mp_if_c`, but the first argument is a type. 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...> ## 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 `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. 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...> ## mp_eval_if<C, T, F, U...>
template<class C, class T, template<class...> class F, class... U> using mp_eval_if = template<class C, class T, template<class...> class F, class... U> using mp_eval_if =
@@ -64,6 +123,11 @@ is to avoid evaluating `F<U...>` when the condition is `true` as it may not be v
Like `mp_eval_if_c`, but the first argument is a type. 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...> ## mp_eval_if_q<C, T, Q, U...>
template<class C, class T, class Q, class... U> using mp_eval_if_q = template<class C, class T, class Q, class... U> using mp_eval_if_q =
@@ -77,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. `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...> ## mp_defer<F, T...>
template<template<class...> class F, class... T> using mp_defer = /*...*/; template<template<class...> class F, class... T> using mp_defer = /*...*/;
@@ -93,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...>`. `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...> ## mp_invoke<Q, T...>
template<class Q, class... T> using mp_invoke = typename Q::template fn<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...>`. `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>>>;
```