diff --git a/README.md b/README.md index 5c7f7fd..e36143f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# mp11 -Simple C++11 metaprogramming library +# mp11, a simple C++11 metaprogramming library -For background, please see the article ["Simple C++11 metaprogramming"](http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html) +For background, please see the article ["Simple C++11 metaprogramming"](http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html). + +The library should be placed in a subdirectory `libs/mp11` in a Boost distribution. There is reference documentation in `doc/html/mp11.html`. diff --git a/doc/html/mp11.html b/doc/html/mp11.html index cfd8531..8994454 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -33,6 +33,14 @@
Table of Contents
<boost/mp11/integral.hpp>
mp_valid<F, T...>
mp_defer<F, T...>
mp_quote<F>
mp_unquote<Q, T...>
mp_quote<F, T...>
mp_invoke<Q, T...>
<boost/mp11/algorithm.hpp>
mp_product<F, L...>
mp_drop_c<L, N>
mp_drop<L, N>
mp_iota_c<L, N>
mp_iota<L, N>
mp_iota_c<N>
mp_iota<N>
mp_at_c<L, I>
mp_at<L,
I>
mp_remove_if<L, P>
mp_partition<L, P>
mp_sort<L, P>
mp_find_index<L, V>
mp_find_index_if<L, P>
mp_find<L, V>
mp_find_if<L, P>
mp_reverse<L>
mp_map_update<M, T, F>
mp_map_erase<M, K>
<boost/mp11/function.hpp>
mp_and<T...>
mp_all<T...>
mp_or<T...>
mp_any<T...>
<boost/integer_sequence.hpp>
integer_sequence<T, I...>
make_integer_sequence<T, N>
index_sequence<I...>
make_index_sequence<N>
index_sequence_for<T...>
<boost/tuple_for_each.hpp>
tuple_for_each
- ... + Mp11 is a C++11 metaprogramming library based on template aliases and variadic + templates. It implements the approach outlined in the article Simple + C++11 metaprogramming (part + 2).
+
+ The general principles upon which Mp11 is built are that algorithms and metafunctions
+ are template aliases of the form F<T...>
+ and data structures are lists of the form L<T...>
,
+ with the library placing no requirements on L
.
+ mp_list<T...>
+ is the built-in list type, but std::tuple<T...>
,
+ std::pair<T1, T2>
and std::variant<T...>
+ are also perfectly legitimate list types, although of course std::pair<T1,
+ T2>
,
+ due to having exactly two elements, is not resizeable and will consequently
+ not work with algorithms that need to add or remove elements.
+
+ Another distinguishing feature of this approach is that lists (L<T...>
) have the same form as metafunctions
+ (F<T...>
)
+ and can therefore be used as such. For example, applying std::add_pointer_t
+ to the list std::tuple<int, float>
by way of mp_transform<std::add_pointer_t,
+ std::tuple<int, float>>
gives us std::tuple<int*, float*>
, but we can also apply mp_list
+ to the same tuple:
+
using R = mp_transform<mp_list, std::tuple<int, float>>; ++
+ and get std::tuple<mp_list<int>, mp_list<float>>
.
+
+ A list is a — possibly but not necessarily variadic — template
+ class whose parameters are all types, for example mp_list<char[],
+ void>
,
+ mp_list<>
,
+ std::tuple<int, float, char>
,
+ std::pair<int, float>
, std::shared_ptr<X>
.
+
+ 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
+ called invoke
, for example
+
struct Q1 { template<class...> using invoke = void; }; +struct Q2 { template<class T> using invoke = T*; }; +struct Q3 { template<class... T> using invoke = 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
+
struct N { static int constexpr value = 2; }; ++
+ Let's suppose that we have written a metafunction result<T,
+ U>
:
+
template<class T> using promote = std::common_type_t<T, int>; +template<class T, class U> using result = std::common_type_t<promote<T>, promote<U>>; ++
+ that ought to represent the result of an arithmetic operation on the integer
+ types T
and U
, for example t
+ + u
.
+ We want to test whether result<T,
+ U>
+ gives correct results for various combinations of T
+ and U
, so we write the function
+
template<class T1, class T2> void test_result() +{ + using T3 = decltype( T1() + T2() ); + using T4 = result<T1, T2>; + + std::cout << ( std::is_same<T3, T4>::value? "[PASS]": "[FAIL]" ) << std::endl; +} ++
+ and then need to call it a substantial number of times: +
+int main() +{ + test_result<char, char>(); + test_result<char, short>(); + test_result<char, int>(); + test_result<char, unsigned>(); + // ... +} ++
+ Writing all those type combinations by hand is unwieldy, error prone, and + worst of all, boring. This is how we can leverage Mp11 to automate the task: +
+#include <boost/mp11.hpp> +#include <boost/tuple_for_each.hpp> +#include <boost/core/demangle.hpp> +#include <type_traits> +#include <iostream> +#include <typeinfo> + +using namespace boost::mp11; + +template<class T> std::string name() +{ + return boost::core::demangle( typeid(T).name() ); +} + +template<class T> using promote = std::common_type_t<T, int>; +template<class T, class U> using result = std::common_type_t<promote<T>, promote<U>>; + +template<class T1, class T2> void test_result( mp_list<T1, T2> const& ) +{ + using T3 = decltype( T1() + T2() ); + using T4 = result<T1, T2>; + + std::cout << ( std::is_same<T3, T4>::value? "[PASS] ": "[FAIL] " ) << name<T1>() << " + " << name<T2>() << " -> " << name<T3>() << ", result: " << name<T4>() << " " << std::endl; +} + +int main() +{ + using L = std::tuple<char, short, int, unsigned, long, unsigned long>; + boost::tuple_for_each( mp_product<mp_list, L, L>(), [](auto&& x){ test_result(x); } ); +} ++
+ C++17 has a standard variant type, called std::variant
.
+ It also defines a function template std::visit
+ that can be used to apply a function to the contained value of one or more
+ variant
s. So for instance,
+ if the variant
v1
contains 1
,
+ and the variant
v2
contains 2.0f
,
+ std::visit(f, v1, v2)
+ will call f(1, 2.0f)
.
+
+ However, std::visit
has one limitation: it cannot return
+ a result unless all possible applications of the function have the same return
+ type. If, for instance, v1
+ and v2
are both of type
+ std::variant<short, int, float>
,
+
std::visit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 ); ++
+ will fail to compile because the result of x
+ + y
+ can be either int
or float
depending on 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 ) +{ + using R = /*...*/; + return std::visit( [&]( auto&&... x ){ return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); }, 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.
+
+ We'll first define a helper quoted metafunction Qret<F>
+ that returns the result of the application of F
+ to arguments of type T...
:
+
template<class F> struct Qret +{ + template<class... T> using invoke = 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.)
+
+ With Qret
in hand, a variant
of the possible return types is
+ just a matter of applying it over the possible combinations of the variant
+ values:
+
using R = mp_product<Qret<F>::template invoke, std::remove_reference_t<V>...>; ++
+ Why does this work? mp_product<F,
+ L1<T1...>, L2<T2...>, ..., Ln<Tn...>>
returns L1<F<U1, U2, ..., Un>, ...>
,
+ where Ui
traverse all possible
+ combinations of list values. Since in our case all Li
+ are std::variant
, the result will also be std::variant
.
+
+ 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,
+
using R = mp_unique<mp_product<Qret<F>::template invoke, std::remove_reference_t<V>...>>; ++
+ and we're done: +
+#include <boost/mp11.hpp> +#include <boost/core/demangle.hpp> +#include <variant> +#include <type_traits> +#include <typeinfo> +#include <iostream> + +using namespace boost::mp11; + +template<class F> struct Qret +{ + template<class... T> using invoke = decltype( std::declval<F>()( std::declval<T>()... ) ); +}; + +template<class F, class... V> auto rvisit( F&& f, V&&... v ) +{ + using R = mp_unique<mp_product<Qret<F>::template invoke, std::remove_reference_t<V>...>>; + return std::visit( [&]( auto&&... x ){ return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); }, std::forward<V>( v )... ); +} + +template<class T> std::string name() +{ + return boost::core::demangle( typeid(T).name() ); +} + +int main() +{ + std::variant<signed char, unsigned char, signed short, unsigned short, int, unsigned> v1( 1 ); + + std::cout << "(" << name<decltype(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::cout << "(" << name<decltype(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 ); + + std::cout << "(" << name<decltype(v3)>() << ")v3: "; + std::visit( []( auto const& x ){ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v3 ); +} ++
+ The contents of the library are in namespace boost::mp11
, unless
+ specified otherwise.
+
+ For an Mp11 integral constant type T
,
+ T::value
is an integral constant in the C++
+ sense.
+
mp_bool<B>
@@ -490,30 +818,32 @@
template<template<class...> class F> struct mp_quote +template<template<class...> class F, class... T> struct mp_quote { - template<class... T> using apply = F<T...>; + template<class... U> using invoke = F<T..., U...>; };-
mp_quote<F>
- transforms the templateF
- into a type. The nested templateapply
- of the result is an alias forF
. +mp_quote<F, T...>
transforms the templateF
into a type. In the common casemp_quote<F>
, + the nested templateinvoke
+ of the result is an alias forF
; + otherwise,invoke<U...>
+ is an alias forF<T..., U...>
, + allowing partial application.
template<class Q, class... T> using mp_unquote = typename Q::template apply<T...>; +template<class Q, class... T> using mp_invoke = typename Q::template invoke<T...>;-
mp_unquote<Q, T...>
, whereQ
- ismp_quote<F>
, +mp_invoke<Q, T...>
evaluates the nested templateinvoke
of a quoted metafunction.mp_invoke<mp_quote<F>, T...>
is an alias forF<T...>
. +mp_invoke<mp_quote<F, T...>, U...>
is an alias forF<T..., U...>
.
template<class L1, class L2> using mp_assign = /*...*/;+
+ mp_assign<L1<T1...>,
+ L2<T2...>>
+ is an alias for L1<T2...>
.
+ That is, it replaces the elements of L1
+ with those of L2
.
+
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
.
+
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>...>
.
+
template<template<class...> class P, template<class...> class F, class L> using mp_transform_if = /*...*/;+
+ mp_transform_if
replaces
+ the elements T
of L
for which mp_to_bool<P<T>>
is mp_true
+ with F<T>
,
+ and returns the result.
+
template<class L, class V> using mp_fill = /*...*/;+
+ mp_fill<L<T...>, V>
+ returns L<V, V, ..., V>
,
+ with the result having the same size as the input.
+
template<class L, class V> using mp_count = /*...*/;+
+ mp_count<L, V>
returns mp_size_t<N>
, where N
+ is the number of elements of L
+ same as V
.
+
template<class L, template<class...> class P> using mp_count_if = /*...*/;+
+ mp_count_f<L, P>
returns mp_size_t<N>
, where N
+ is the number of elements T
+ of L
for which mp_to_bool<P<T>>
+ is mp_true
.
+
template<class L, class V> using mp_contains = mp_to_bool<mp_count<L, V>>;+
+ mp_contains<L, V>
is mp_true
+ when L
contains an element
+ V
, mp_false
+ otherwise.
+
template<class L, std::size_t N> using mp_repeat_c = /*...*/;+
+ mp_repeat_c<L, N>
returns a list of the same type as
+ L
that consists of N
concatenated copies of L
.
+
template<class L, class N> using mp_repeat = /*...*/;+
+ Same as mp_repeat_c
but
+ with a type argument N
.
+ The number of copies is N::value
+ and must be nonnegative.
+
template<template<class...> class F, class... L> using mp_product = /*...*/;+
+ mp_product<F, L1<T1...>, L2<T2...>, ...,
+ Ln<Tn...>>
+ evaluates F<U1, U2, ..., Un>
for values Ui
+ taken from 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
.
+
template<class L, std::size_t N> using mp_drop_c = /*...*/;+
+ mp_drop_c<L, N>
removes the first N
+ elements of L
and returns
+ the result.
+
template<class L, class N> using mp_drop = /*...*/;+
+ Same as mp_drop_c
, but
+ with a type argument N
.
+ N::value
must be a nonnegative number.
+
template<std::size_t N> using mp_iota_c = /*...*/;+
+ mp_iota_c<N>
+ is an alias for mp_list<mp_size_t<0>,
+ mp_size_t<1>, ...,
+ mp_size_t<N-1>>
.
+
template<class N> using mp_iota = /*...*/;+
+ Same as mp_iota_c
, but
+ with a type argument N
.
+ N::value
must be a nonnegative number. Returns
+ mp_list<std::integral_constant<T, 0>, std::integral_constant<T, 1>,
+ ..., std::integral_constant<T, N::value-1>>
+ 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.
+
template<class L, class I> using mp_at = /*...*/;+
+ Same as mp_at_c
, but with
+ a type argument I
. I::value
+ must be a nonnegative number.
+
template<class L, std::size_t N> using mp_take_c = /*...*/;+
+ mp_take_c<L, N>
returns a list of the same type as
+ L
containing the first
+ N
elements of L
.
+
template<class L, class N> using mp_take = /*...*/;+
+ Same as mp_take_c
, but
+ with a type argument N
.
+ N::value
must be a nonnegative number.
+
template<class L, class V, class W> using mp_replace = /*...*/;+
+ Replaces all V
elements
+ of L
with W
and returns the result.
+
template<class L, template<class...> class P, class W> using mp_replace_if = /*...*/;+
+ Replaces all T
elements
+ of L
for which mp_to_bool<P<T>>
+ is mp_true
with W
and returns the result.
+
template<class L, template<class...> class P> using mp_copy_if = /*...*/;+
+ Copies the elements T
of
+ L
for which mp_to_bool<P<T>>
+ is mp_true
to a new list
+ of the same type and returns it.
+
template<class L, class V> using mp_remove = /*...*/;+
+ Removes all V
elements
+ of L
and returns the result.
+
template<class L, template<class...> class P> using mp_remove_if = /*...*/;+
+ Removes all elements T
+ of L
for which mp_to_bool<P<T>>
+ is mp_true
and returns
+ the result.
+
template<class L, template<class...> class P> using mp_partition = /*...*/;+
+ mp_partition<L<T...>, P>
+ partitions L
into two lists
+ L<U1...>
+ and L<U2...>
+ such that mp_to_bool<P<T>>
+ is mp_true
for the elements
+ of L<U1...>
+ and mp_false
for the elements
+ of L<U2...>
.
+ Returns L<L<U1...>,
+ L<U2...>>
.
+
template<class L, template<class...> class P> using mp_sort = /*...*/;-
template<class L, class V> using mp_find_index = /*...*/; --
template<class L, template<class...> class P> using mp_find_index_if = /*...*/; -+
+ mp_sort<L, P>
sorts the list L
+ according to the strict weak ordering mp_to_bool<P<T, U>>
.
+
template<class L, class V> using mp_find = mp_drop<L, mp_find_index<L, V>>; +template<class L, class V> using mp_find = /*...*/;++
mp_find<L, V>
is an alias formp_size_t<I>
, whereI
+ is the zero-based index of the first occurence ofV
+ inL
. IfL
does not containV
, +mp_find<L, V>
ismp_size<L>
. +
template<class L, template<class...> class P> using mp_find_if = mp_drop<L, mp_find_index_if<L, P>>; +template<class L, template<class...> class P> using mp_find_if = /*...*/;++
mp_find_f<L, P>
is an alias formp_size_t<I>
, whereI
+ is the zero-based index of the first elementT
+ inL
for whichmp_to_bool<P<T>>
+ ismp_true
. If there is + no such element,mp_find<L, V>
+ ismp_size<L>
. +
template<class L> using mp_reverse = /*...*/;+
+ mp_reverse<L<T1, T2, ..., Tn>>
is L<Tn, ..., T2, T1>
.
+
template<class L, class V, template<class...> class F> using mp_fold = /*...*/;+
+ mp_fold<L<T1, T2, ..., Tn>, V, F>
+ is F<
+ F<
+ F<
+ F<V, T1>, T2>, ...>,
+ Tn>
,
+ or V
, if L
is empty.
+
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.
+
template<class L> using mp_unique = /*...*/;+
+ mp_unique<L>
+ returns a list of the same type as L
+ with the duplicate elements removed.
+
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
.
+
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
.
+
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
.
+
mp_and<T...>
+template<class... T> using mp_and = /*...*/; ++
+ mp_and<T...>
+ is an alias for the first type U
+ in T...
+ for which mp_to_bool<U>
+ is mp_false
. If no such
+ type exists, the last one is returned. mp_and<>
is mp_true
.
+ Similar to std::conjunction
in C++17.
+
mp_all<T...>
+template<class... T> using mp_all = /*...*/; ++
+ mp_all<T...>
+ is mp_true
if mp_to_bool<U>
+ is mp_true
for all types
+ U
in T...
, mp_false
+ otherwise. Same as mp_and
,
+ but does not perform short-circuit evaluation. mp_and<mp_false, void>
+ is mp_false
, but mp_all<mp_false, void>
+ is an error because void
does
+ not have a nested value
.
+ The upside is that mp_all
+ is faster.
+
mp_or<T...>
+template<class... T> using mp_or = /*...*/; ++
+ mp_or<T...>
+ is an alias for the first type U
+ in T...
+ for which mp_to_bool<U>
+ is mp_true
. If no such
+ type exists, the last one is returned. mp_or<>
is mp_false
.
+ Similar to std::disjunction
in C++17.
+
mp_any<T...>
+template<class... T> using mp_any = /*...*/; ++
+ 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.
+
+ The contents of this header are defined in namespace boost
.
+
template<class T, T... I> struct integer_sequence +{ +}; ++
+ integer_sequence<T, I...>
holds a sequence of integers of
+ type T
. Same as C++14's
+ std::integer_sequence
.
+
template<class T, T N> using make_integer_sequence = /*...*/; ++
+ make_integer_sequence<T, N>
is integer_sequence<T, 0,
+ 1, ..., N-1>
.
+ Same as C++14's std::make_integer_sequence
.
+
template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>; ++
+ index_sequence<I...>
+ is an alias for integer_sequence<size_t, I...>
.
+ Same as C++14's std::index_sequence
.
+
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
.
+
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
.
+
+ The contents of this header are defined in namespace boost
.
+
template<class Tp, class F> constexpr F tuple_for_each(Tp&& tp, F&& f); ++
+ tuple_for_each(tp, f)
applies
+ the function object f
to
+ each element of tp
by evaluating
+ the expression f(std::get<J>(std::forward<Tp>(tp)))
+ for J
in 0..N-1
,
+ where N
is std::tuple_size<std::remove_reference_t<Tp>>::value
.
+
+ Returns std::forward<F>(f)
.
+
Last revised: March 14, 2017 at 20:19:38 GMT |
+Last revised: March 17, 2017 at 03:26:57 GMT |
`]
template class P, template >` is `mp_true` with `F >` is `mp_true`.
[endsect]
[section `mp_contains >` is `mp_true` with `W` and returns the result.
[endsect]
[section `mp_copy_if >` is `mp_true` to a new list of the same type and returns it.
[endsect]
[section `mp_remove >` is `mp_true` and returns the result.
[endsect]
[section `mp_partition >` is `mp_true`
+for the elements of `L >`.
[endsect]
[section `mp_find >` is `mp_true`. If there is no such element, `mp_find