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
+ variants. 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 templateFinto 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 templateinvokeof 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 Ith
+ 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. IfLdoes 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+ inLfor 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