From e67e6ce89dc51880d80310296717cbecfc0c1014 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 18 May 2017 14:20:58 +0300 Subject: [PATCH 01/12] Document mp_same --- doc/html/mp11.html | 18 +++++++++++++++++- doc/mp11/function.qbk | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/html/mp11.html b/doc/html/mp11.html index 64d4482..53c0e68 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -166,6 +166,7 @@
mp_all<T...>
mp_or<T...>
mp_any<T...>
+
mp_same<T...>
Bind, <boost/mp11/bind.hpp>
@@ -2087,6 +2088,21 @@ but does not perform short-circuit evaluation.

+
+ +
template<class... T> using mp_same = /*...*/;
+
+

+ mp_same<T...> + is mp_true if all the types + in T... + are the same type, mp_false + otherwise. mp_same<> + is mp_true. +

+

@@ -2299,7 +2315,7 @@

- +

Last revised: May 17, 2017 at 17:44:23 GMT

Last revised: May 18, 2017 at 11:17:12 GMT


diff --git a/doc/mp11/function.qbk b/doc/mp11/function.qbk index b0ef1ae..0ccdc49 100644 --- a/doc/mp11/function.qbk +++ b/doc/mp11/function.qbk @@ -37,4 +37,10 @@ If no such type exists, the last one is returned. `mp_or<>` is `mp_false`. Simil `mp_or`, but does not perform short-circuit evaluation. [endsect] +[section `mp_same`] + template using mp_same = /*...*/; + +`mp_same` is `mp_true` if all the types in `T...` are the same type, `mp_false` otherwise. `mp_same<>` is `mp_true`. +[endsect] + [endsect:function] From 37bbb15375157f2fd8a3580b229b3471d18d099d Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 18 May 2017 14:59:46 +0300 Subject: [PATCH 02/12] Make the E parameter of mp_if optional --- doc/html/mp11.html | 26 ++++++++++++++------------ doc/mp11/utility.qbk | 13 +++++++++---- include/boost/mp11/utility.hpp | 10 ++++++---- test/Jamfile.v2 | 1 + test/mp_if_sf.cpp | 24 ++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 20 deletions(-) create mode 100644 test/mp_if_sf.cpp diff --git a/doc/html/mp11.html b/doc/html/mp11.html index 53c0e68..72adda5 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -84,7 +84,7 @@
mp_identity<T>
mp_identity_t<T>
mp_inherit<T...>
-
mp_if_c<B, T, E>
+
mp_if_c<B, T, E...>
mp_if<C, T, E>
mp_eval_if_c<B, T, F, @@ -1210,16 +1210,20 @@
-
template<bool C, class T, class E> using mp_if_c = /*...*/;
+
template<bool C, class T, class... E> using mp_if_c = /*...*/;
 

- mp_if_c<B, T, E> - is an alias for T when - B is true, - for E otherwise. + mp_if_c<true, T, E...> + is an alias for T. mp_if_c<false, T, E> is an alias for E. + Otherwise, the result is a substitution failure.

+
using R1 = mp_if_c<true, int, void>;  // int
+using R2 = mp_if_c<flase, int, void>; // void
+
+template<class I> using void_if_5 = mp_if_c<I::value == 5, void>; // `void` when `I::value` is 5, substitution failure otherwise
+

@@ -1229,10 +1233,8 @@
template<class C, class T, class E> using mp_if = mp_if_c<static_cast<bool>(C::value), T, E>;
 

- mp_if<C, T, E> - is an alias for T when - C::value is true, - for E otherwise. + Like mp_if_c, but the first + argument is a type.

@@ -2315,7 +2317,7 @@
- +

Last revised: May 18, 2017 at 11:17:12 GMT

Last revised: May 18, 2017 at 11:57:59 GMT


diff --git a/doc/mp11/utility.qbk b/doc/mp11/utility.qbk index 1786265..23d6ce1 100644 --- a/doc/mp11/utility.qbk +++ b/doc/mp11/utility.qbk @@ -23,16 +23,21 @@ template struct mp_inherit: T... {}; [endsect] -[section `mp_if_c`] - template using mp_if_c = /*...*/; +[section `mp_if_c`] + template using mp_if_c = /*...*/; -`mp_if_c` is an alias for `T` when `B` is `true`, for `E` otherwise. +`mp_if_c` is an alias for `T`. `mp_if_c` is an alias for `E`. Otherwise, the result is a substitution failure. + + using R1 = mp_if_c; // int + using R2 = mp_if_c; // void + + template using void_if_5 = mp_if_c; // `void` when `I::value` is 5, substitution failure otherwise [endsect] [section `mp_if`] template using mp_if = mp_if_c(C::value), T, E>; -`mp_if` is an alias for `T` when `C::value` is `true`, for `E` otherwise. +Like `mp_if_c`, but the first argument is a type. [endsect] [section `mp_eval_if_c`] diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index e220b5f..0fe8853 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -33,9 +33,11 @@ template struct mp_inherit: T... {}; namespace detail { -template struct mp_if_c_impl; +template struct mp_if_c_impl +{ +}; -template struct mp_if_c_impl +template struct mp_if_c_impl { using type = T; }; @@ -47,8 +49,8 @@ template struct mp_if_c_impl } // namespace detail -template using mp_if_c = typename detail::mp_if_c_impl::type; -template using mp_if = typename detail::mp_if_c_impl(C::value), T, E>::type; +template using mp_if_c = typename detail::mp_if_c_impl::type; +template using mp_if = typename detail::mp_if_c_impl(C::value), T, E...>::type; // mp_eval_if, mp_eval_if_c namespace detail diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index da5c569..579fc86 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -75,6 +75,7 @@ run integral.cpp : : : $(REQ) ; run mp_identity.cpp : : : $(REQ) ; run mp_inherit.cpp : : : $(REQ) ; run mp_if.cpp : : : $(REQ) ; +run mp_if_sf.cpp : : : $(REQ) ; run mp_eval_if.cpp : : : $(REQ) ; run mp_valid.cpp : : : $(REQ) ; run mp_defer.cpp : : : $(REQ) ; diff --git a/test/mp_if_sf.cpp b/test/mp_if_sf.cpp new file mode 100644 index 0000000..15ef309 --- /dev/null +++ b/test/mp_if_sf.cpp @@ -0,0 +1,24 @@ + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + + +#include +#include +#include + +int main() +{ + using boost::mp11::mp_if; + using boost::mp11::mp_valid; + + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + + return boost::report_errors(); +} From 611ea810bd37cb1b8410775b88191d8c92d56bbf Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 18 May 2017 15:22:05 +0300 Subject: [PATCH 03/12] SFINAE-fail mp_eval_if when F is invalid --- include/boost/mp11/utility.hpp | 41 +++++++++++++++++----------------- test/Jamfile.v2 | 1 + test/mp_eval_if_sf.cpp | 34 ++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 test/mp_eval_if_sf.cpp diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index 0fe8853..5590e09 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -52,27 +52,6 @@ template struct mp_if_c_impl template using mp_if_c = typename detail::mp_if_c_impl::type; template using mp_if = typename detail::mp_if_c_impl(C::value), T, E...>::type; -// mp_eval_if, mp_eval_if_c -namespace detail -{ - -template class F, class... U> struct mp_eval_if_c_impl; - -template class F, class... U> struct mp_eval_if_c_impl -{ - using type = T; -}; - -template class F, class... U> struct mp_eval_if_c_impl -{ - using type = F; -}; - -} // namespace detail - -template class F, class... U> using mp_eval_if_c = typename detail::mp_eval_if_c_impl::type; -template class F, class... U> using mp_eval_if = typename detail::mp_eval_if_c_impl(C::value), T, F, U...>::type; - // mp_valid // implementation by Bruno Dutra (by the name is_evaluable) namespace detail @@ -107,6 +86,26 @@ struct mp_no_type template class F, class... T> using mp_defer = mp_if, detail::mp_defer_impl, detail::mp_no_type>; +// mp_eval_if, mp_eval_if_c +namespace detail +{ + +template class F, class... U> struct mp_eval_if_c_impl; + +template class F, class... U> struct mp_eval_if_c_impl +{ + using type = T; +}; + +template class F, class... U> struct mp_eval_if_c_impl: mp_defer +{ +}; + +} // namespace detail + +template class F, class... U> using mp_eval_if_c = typename detail::mp_eval_if_c_impl::type; +template class F, class... U> using mp_eval_if = typename detail::mp_eval_if_c_impl(C::value), T, F, U...>::type; + // mp_quote template class F> struct mp_quote { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 579fc86..ef473d6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -77,6 +77,7 @@ run mp_inherit.cpp : : : $(REQ) ; run mp_if.cpp : : : $(REQ) ; run mp_if_sf.cpp : : : $(REQ) ; run mp_eval_if.cpp : : : $(REQ) ; +run mp_eval_if_sf.cpp : : : $(REQ) ; run mp_valid.cpp : : : $(REQ) ; run mp_defer.cpp : : : $(REQ) ; run mp_quote.cpp : : : $(REQ) ; diff --git a/test/mp_eval_if_sf.cpp b/test/mp_eval_if_sf.cpp new file mode 100644 index 0000000..8eca74e --- /dev/null +++ b/test/mp_eval_if_sf.cpp @@ -0,0 +1,34 @@ + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + + +#include +#include +#include + +using boost::mp11::mp_eval_if; +using boost::mp11::mp_identity_t; + +template using eval_if = mp_eval_if; + +int main() +{ + using boost::mp11::mp_valid; + + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + + return boost::report_errors(); +} From 02385ac83dd067daf3d30ee9a6fbb759cfc0041f Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Thu, 18 May 2017 16:13:55 +0300 Subject: [PATCH 04/12] SFINAE-fail mp_transform on lists of different sizes --- include/boost/mp11/algorithm.hpp | 27 +++++++++++++++----- test/Jamfile.v2 | 1 + test/mp_transform_sf.cpp | 42 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 test/mp_transform_sf.cpp diff --git a/include/boost/mp11/algorithm.hpp b/include/boost/mp11/algorithm.hpp index 298c25a..a91aeec 100644 --- a/include/boost/mp11/algorithm.hpp +++ b/include/boost/mp11/algorithm.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,9 @@ template class F> using mp_fold = typename namespace detail { -template class F, class... L> struct mp_transform_impl; +template class F, class... L> struct mp_transform_impl +{ +}; template class F, template class L, class... T> struct mp_transform_impl> { @@ -68,19 +71,33 @@ template class F, template class L, class... T> str template class F, template class L1, class... T1, template class L2, class... T2> struct mp_transform_impl, L2> { - static_assert( sizeof...(T1) == sizeof...(T2), "The arguments of mp_transform should be of the same size" ); using type = L1...>; }; template class F, template class L1, class... T1, template class L2, class... T2, template class L3, class... T3> struct mp_transform_impl, L2, L3> { - static_assert( sizeof...(T1) == sizeof...(T2) && sizeof...(T1) == sizeof...(T3), "The arguments of mp_transform should be of the same size" ); using type = L1...>; }; +#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) + +template using mp_same_size_1 = mp_same...>; +template struct mp_same_size_2: mp_defer {}; + +#endif + } // namespace detail -template class F, class... L> using mp_transform = typename detail::mp_transform_impl::type; +#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) + +template class F, class... L> using mp_transform = typename mp_if::type, detail::mp_transform_impl>::type; + +#else + +template class F, class... L> using mp_transform = typename mp_if...>, detail::mp_transform_impl>::type; + +#endif + template using mp_transform_q = mp_transform; namespace detail @@ -88,8 +105,6 @@ namespace detail template class F, template class L1, class... T1, template class L2, class... T2, template class L3, class... T3, template class L4, class... T4, class... L> struct mp_transform_impl, L2, L3, L4, L...> { - static_assert( sizeof...(T1) == sizeof...(T2) && sizeof...(T1) == sizeof...(T3) && sizeof...(T1) == sizeof...(T4), "The arguments of mp_transform should be of the same size" ); - using A1 = L1...>; template using _f = mp_transform; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ef473d6..f45c9e6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -35,6 +35,7 @@ run mp_apply_q.cpp : : : $(REQ) ; run mp_assign.cpp : : : $(REQ) ; run mp_clear.cpp : : : $(REQ) ; run mp_transform.cpp : : : $(REQ) ; +run mp_transform_sf.cpp : : : $(REQ) ; run mp_transform_if.cpp : : : $(REQ) ; run mp_fill.cpp : : : $(REQ) ; run mp_count.cpp : : : $(REQ) ; diff --git a/test/mp_transform_sf.cpp b/test/mp_transform_sf.cpp new file mode 100644 index 0000000..7f18eab --- /dev/null +++ b/test/mp_transform_sf.cpp @@ -0,0 +1,42 @@ + +// Copyright 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + + +#include +#include +#include +#include + +using boost::mp11::mp_transform; +using boost::mp11::mp_list; +using boost::mp11::mp_valid; + +template using F = void; + +template using transform = mp_transform; + +int main() +{ + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + +#if !BOOST_WORKAROUND( BOOST_MSVC, <= 1800 ) + BOOST_TEST_TRAIT_TRUE((mp_valid>)); +#endif + + BOOST_TEST_TRAIT_FALSE((mp_valid, mp_list>)); + BOOST_TEST_TRAIT_FALSE((mp_valid, mp_list<>, mp_list>)); + BOOST_TEST_TRAIT_FALSE((mp_valid, mp_list<>, mp_list<>, mp_list>)); + BOOST_TEST_TRAIT_FALSE((mp_valid, mp_list<>, mp_list<>, mp_list<>, mp_list>)); + + return boost::report_errors(); +} From ec9d43e7e923ffbed9c5d4f6ec862f36e8e12f62 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 01:00:56 +0300 Subject: [PATCH 05/12] Try to fix g++ 4.7 --- include/boost/mp11/algorithm.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/mp11/algorithm.hpp b/include/boost/mp11/algorithm.hpp index a91aeec..cd502f6 100644 --- a/include/boost/mp11/algorithm.hpp +++ b/include/boost/mp11/algorithm.hpp @@ -88,7 +88,7 @@ template struct mp_same_size_2: mp_defer {}; } // namespace detail -#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) +#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) || BOOST_WORKAROUND( BOOST_GCC, < 40800 ) template class F, class... L> using mp_transform = typename mp_if::type, detail::mp_transform_impl>::type; From 3cc44323e940e1eb6d796816bbf54a4f1c249c5b Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 01:13:09 +0300 Subject: [PATCH 06/12] Second try to fix g++ 4.7 --- include/boost/mp11/algorithm.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/mp11/algorithm.hpp b/include/boost/mp11/algorithm.hpp index cd502f6..1762502 100644 --- a/include/boost/mp11/algorithm.hpp +++ b/include/boost/mp11/algorithm.hpp @@ -79,7 +79,7 @@ template class F, template class L1, class... T1, t using type = L1...>; }; -#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) +#if BOOST_WORKAROUND( BOOST_MSVC, == 1900 ) || BOOST_WORKAROUND( BOOST_GCC, < 40800 ) template using mp_same_size_1 = mp_same...>; template struct mp_same_size_2: mp_defer {}; From 7d91a174a2881b481a81c075b97b76bf3ee7603c Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 01:43:30 +0300 Subject: [PATCH 07/12] Fix tuple_cat example a bit --- doc/html/mp11.html | 12 ++++++------ doc/mp11/examples.qbk | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/html/mp11.html b/doc/html/mp11.html index 72adda5..103067c 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -474,7 +474,7 @@
std::array<int, 2> t1{ 1, 2 };
 std::array<float, 3> t2{ 3.0f, 4.0f, 5.0f };
 
-auto result = ::tuple_cat( t1, std::move( t2 ) );
+auto result = ::tuple_cat( t1, t2 );
 

Let's fix these one by one. Support for move-only types is easy, if one knows @@ -542,11 +542,11 @@

What we need is, given a tuple-like type Tp, to obtain mp_list<std::tuple_element<0, - Tp>, - std::tuple_element<1, Tp>, ..., std::tuple_element<N-1, Tp>>, where N + Tp>::type, std::tuple_element<1, + Tp>::type, ..., std::tuple_element<N-1, Tp>::type>, where N is tuple_size<Tp>::value. Here's one way to do it:

-
template<class T, class I> using tuple_element = std::tuple_element_t<I::value, T>;
+
template<class T, class I> using tuple_element = typename std::tuple_element<I::value, T>::type;
 template<class T> using from_tuple_like = mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
 

@@ -591,7 +591,7 @@ template<class T> using remove_cv_ref = typename std::remove_cv< typename std::remove_reference<T>::type>::type; -template<class T, class I> using tuple_element = std::tuple_element_t<I::value, T>; +template<class T, class I> using tuple_element = typename std::tuple_element<I::value, T>::type; template<class T> using from_tuple_like = mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>; template<class... Tp, @@ -2317,7 +2317,7 @@

- +

Last revised: May 18, 2017 at 11:57:59 GMT

Last revised: May 18, 2017 at 22:41:31 GMT


diff --git a/doc/mp11/examples.qbk b/doc/mp11/examples.qbk index 9d85678..9394bd3 100644 --- a/doc/mp11/examples.qbk +++ b/doc/mp11/examples.qbk @@ -148,7 +148,7 @@ that support `tuple_size`, `tuple_element`, and `get`), while our implementation std::array t1{ 1, 2 }; std::array t2{ 3.0f, 4.0f, 5.0f }; - auto result = ::tuple_cat( t1, std::move( t2 ) ); + auto result = ::tuple_cat( t1, t2 ); Let's fix these one by one. Support for move-only types is easy, if one knows where to look. The problem is that `Tp` that we're passing to the helper `tuple_cat_` is (correctly) `tuple&&, unique_ptr&&>`, @@ -193,10 +193,10 @@ the corresponding `mp_list`. Technically, a more principled approach would've been to return `std::tuple`, but here `mp_list` will prove more convenient. -What we need is, given a tuple-like type `Tp`, to obtain `mp_list, std::tuple_element<1, Tp>, -..., std::tuple_element>`, where `N` is `tuple_size::value`. Here's one way to do it: +What we need is, given a tuple-like type `Tp`, to obtain `mp_list::type, std::tuple_element<1, Tp>::type, +..., std::tuple_element::type>`, where `N` is `tuple_size::value`. Here's one way to do it: - template using tuple_element = std::tuple_element_t; + template using tuple_element = typename std::tuple_element::type; template using from_tuple_like = mp_product, mp_iota>>; (`mp_iota` is an algorithm that returns an `mp_list` with elements `mp_size_t<0>`, `mp_size_t<1>`, ..., `mp_size_t`.) @@ -224,7 +224,7 @@ With all these fixes applied, our fully operational `tuple_cat` now looks like t template using remove_cv_ref = typename std::remove_cv< typename std::remove_reference::type>::type; - template using tuple_element = std::tuple_element_t; + template using tuple_element = typename std::tuple_element::type; template using from_tuple_like = mp_product, mp_iota>>; template Date: Fri, 19 May 2017 02:43:47 +0300 Subject: [PATCH 08/12] Add another example --- doc/html/mp11.html | 100 +++++++++++++++++++++++++++++++++++++++++- doc/mp11/examples.qbk | 56 +++++++++++++++++++++++ 2 files changed, 155 insertions(+), 1 deletion(-) diff --git a/doc/html/mp11.html b/doc/html/mp11.html index 103067c..9f17fb1 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -38,6 +38,8 @@
Generating Test Cases
+
Writing + common_type specializations
Fixing tuple_cat
Computing Return Types
@@ -401,6 +403,102 @@
+

+ 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 + ?: operator) is unsuitable. +

+

+ Let's write a common_type + specialization for two std::tuple + arguments. For that, we need a metafunction that applies std::common_type + to each pair of elements and gathers the results into a tuple: +

+
template<class Tp1, class Tp2> using common_tuple = mp_transform<std::common_type_t, Tp1, Tp2>;
+
+

+ then specialize common_type + to use it: +

+
namespace std
+{
+
+    template<class... T1, class... T2> struct common_type<std::tuple<T1...>, std::tuple<T2...>>: mp_defer<common_tuple, std::tuple<T1...>, std::tuple<T2...>>
+    {
+    };
+
+} // std
+
+

+ (There is no need to specialize std::common_type + for more than two arguments - it takes care of synthesizing the appropriate + semantics from the binary case.) +

+

+ The subtlety here is the use of mp_defer. + We could have defined a nested type + to common_tuple<std::tuple<T1...>, std::tuple<T2...>>, and it would still have worked + in all valid cases. By letting mp_defer + define type, though, we make + our specialization SFINAE-friendly. +

+

+ That is, when our common_tuple + causes a substitution failure instead of a hard error, mp_defer + will not define a nested type, + and common_type_t, which + is defined as typename common_type<...>::type, + will also cause a substitution failure. +

+

+ As another example, consider the hypothetical type expected<T, + E...> + that represents either a successful return with a value of T, or an unsucessful return with an error + code of some type in the list E.... The common type of expected<T1, E1, + E2, E3> + and expected<T2, E1, E4, + E5> + is expected<std::common_type_t<T1, T2>, + E1, E2, E3, E4, + E5>. + That is, the possible return values are combined into their common type, + and we take the union of the set of error types. +

+

+ Therefore, +

+
template<class T1, class E1, class T2, class E2> using common_expected = mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, std::common_type_t<T1, T2>>, expected>;
+
+namespace std
+{
+
+    template<class T1, class... E1, class T2, class... E2> struct common_type<expected<T1, E1...>, expected<T2, E2...>>: mp_defer<common_expected, T1, mp_list<E1...>, T2, mp_list<E2...>>
+    {
+    };
+
+} // std
+
+

+ Here we've taken a different tack; instead of passing the expected + types to common_expected, + we're passing the T types + and lists of the E types. + This makes our job easier. mp_unique<mp_append<E1, E2>> + gives us the concatenation of E1 + and E2 with the duplicates + removed; we then add std::common_type_t<T1, T2> + to the front via mp_push_front; + and finally, we mp_rename + the resultant mp_list to + expected. +

+
+
+

@@ -2317,7 +2415,7 @@

- +

Last revised: May 18, 2017 at 22:41:31 GMT

Last revised: May 18, 2017 at 23:36:45 GMT


diff --git a/doc/mp11/examples.qbk b/doc/mp11/examples.qbk index 9394bd3..93aad48 100644 --- a/doc/mp11/examples.qbk +++ b/doc/mp11/examples.qbk @@ -87,6 +87,62 @@ tuple element; we use a (C++14) lambda that calls `test_result`. (In pure C++11, function object with a templated `operator()` and pass that to `tuple_for_each` directly.) [endsect] +[section Writing `common_type` specializations] + +The standard trait `std::common_type`, used to obtain a type to which all of its arguments can convert without +unnecessary loss of precision, can be user-specialized when its default implementation (based on the ternary `?:` +operator) is unsuitable. + +Let's write a `common_type` specialization for two `std::tuple` arguments. For that, we need a metafunction that +applies `std::common_type` to each pair of elements and gathers the results into a tuple: + + template using common_tuple = mp_transform; + +then specialize `common_type` to use it: + + namespace std + { + + template struct common_type, std::tuple>: mp_defer, std::tuple> + { + }; + + } // std + +(There is no need to specialize `std::common_type` for more than two arguments - it takes care of synthesizing the appropriate semantics from +the binary case.) + +The subtlety here is the use of `mp_defer`. We could have defined a nested `type` to `common_tuple, std::tuple>`, +and it would still have worked in all valid cases. By letting `mp_defer` define `type`, though, we make our specialization /SFINAE-friendly/. + +That is, when our `common_tuple` causes a substitution failure instead of a hard error, `mp_defer` will not define a nested `type`, +and `common_type_t`, which is defined as `typename common_type<...>::type`, will also cause a substitution failure. + +As another example, consider the hypothetical type `expected` that represents either a successful return with a value of `T`, +or an unsucessful return with an error code of some type in the list `E...`. The common type of `expected` and +`expected` is `expected, E1, E2, E3, E4, E5>`. That is, the possible return values are +combined into their common type, and we take the union of the set of error types. + +Therefore, + + template using common_expected = mp_rename>, std::common_type_t>, expected>; + + namespace std + { + + template struct common_type, expected>: mp_defer, T2, mp_list> + { + }; + + } // std + +Here we've taken a different tack; instead of passing the `expected` types to `common_expected`, we're passing the `T` types and lists of +the `E` types. This makes our job easier. `mp_unique>` gives us the concatenation of `E1` and `E2` with the duplicates +removed; we then add `std::common_type_t` to the front via `mp_push_front`; and finally, we `mp_rename` the resultant `mp_list` +to `expected`. + +[endsect] + [section Fixing `tuple_cat`] The article [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html Simple C++11 metaprogramming] builds an From 1d1be2bbf4f88af31acd05d9d1a5757a7992f3b1 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 03:22:53 +0300 Subject: [PATCH 09/12] Add mp_eval_if_q --- doc/html/mp11.html | 36 +++++++++++++++++++++++----------- doc/mp11/utility.qbk | 16 ++++++++++----- include/boost/mp11/utility.hpp | 14 +++---------- test/mp_eval_if.cpp | 15 +++++++++++++- test/mp_eval_if_sf.cpp | 19 +++++++++++++++--- 5 files changed, 69 insertions(+), 31 deletions(-) diff --git a/doc/html/mp11.html b/doc/html/mp11.html index 9f17fb1..706b518 100644 --- a/doc/html/mp11.html +++ b/doc/html/mp11.html @@ -86,13 +86,15 @@
mp_identity<T>
mp_identity_t<T>
mp_inherit<T...>
-
mp_if_c<B, T, E...>
+
mp_if_c<C, T, E...>
mp_if<C, - T, E>
-
mp_eval_if_c<B, T, F, + T, E...>
+
mp_eval_if_c<C, T, F, U...>
mp_eval_if<C, T, F, U...>
+
mp_eval_if_q<C, T, Q, + U...>
mp_valid<F, T...>
mp_defer<F, T...>
mp_quote<F>
@@ -1308,7 +1310,7 @@
template<bool C, class T, class... E> using mp_if_c = /*...*/;
 
@@ -1325,10 +1327,10 @@
-
template<class C, class T, class E> using mp_if = mp_if_c<static_cast<bool>(C::value), T, E>;
+
template<class C, class T, class E...> using mp_if = mp_if_c<static_cast<bool>(C::value), T, E...>;
 

Like mp_if_c, but the first @@ -1337,16 +1339,16 @@

template<bool C, class T, template<class...> class F, class... U> using mp_eval_if_c = /*...*/;
 

- mp_eval_if_c<B, T, F, + mp_eval_if_c<C, T, F, U...> is an alias for T when - B is true, + 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.

@@ -1365,6 +1367,18 @@
+
template<class C, class T, class Q, class... U> using mp_eval_if_q = mp_eval_if<C, T, Q::template fn, U...>;
+
+

+ Like mp_eval_if, but takes + a quoted metafunction. +

+
+
+
template<template<class...> class F, class... T> using mp_valid = /*...*/;
@@ -2415,7 +2429,7 @@
 
- +

Last revised: May 18, 2017 at 23:36:45 GMT

Last revised: May 18, 2017 at 23:57:52 GMT


diff --git a/doc/mp11/utility.qbk b/doc/mp11/utility.qbk index 23d6ce1..13b6f80 100644 --- a/doc/mp11/utility.qbk +++ b/doc/mp11/utility.qbk @@ -23,7 +23,7 @@ template struct mp_inherit: T... {}; [endsect] -[section `mp_if_c`] +[section `mp_if_c`] template using mp_if_c = /*...*/; `mp_if_c` is an alias for `T`. `mp_if_c` is an alias for `E`. Otherwise, the result is a substitution failure. @@ -34,16 +34,16 @@ template using void_if_5 = mp_if_c; // `void` when `I::value` is 5, substitution failure otherwise [endsect] -[section `mp_if`] - template using mp_if = mp_if_c(C::value), T, E>; +[section `mp_if`] + template using mp_if = mp_if_c(C::value), T, E...>; Like `mp_if_c`, but the first argument is a type. [endsect] -[section `mp_eval_if_c`] +[section `mp_eval_if_c`] template class F, class... U> using mp_eval_if_c = /*...*/; -`mp_eval_if_c` is an alias for `T` when `B` is `true`, for `F` otherwise. Its purpose +`mp_eval_if_c` is an alias for `T` when `C` is `true`, for `F` otherwise. Its purpose is to avoid evaluating `F` when the condition is `true` as it may not be valid in this case. [endsect] @@ -53,6 +53,12 @@ is to avoid evaluating `F` when the condition is `true` as it may not be v Like `mp_eval_if_c`, but the first argument is a type. [endsect] +[section `mp_eval_if_q`] + template using mp_eval_if_q = mp_eval_if; + +Like `mp_eval_if`, but takes a quoted metafunction. +[endsect] + [section `mp_valid`] template class F, class... T> using mp_valid = /*...*/; diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index 5590e09..1439596 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -105,23 +105,15 @@ template class F, class... U> struct mp_eval_if_c_im template class F, class... U> using mp_eval_if_c = typename detail::mp_eval_if_c_impl::type; template class F, class... U> using mp_eval_if = typename detail::mp_eval_if_c_impl(C::value), T, F, U...>::type; +template using mp_eval_if_q = typename detail::mp_eval_if_c_impl(C::value), T, Q::template fn, U...>::type; // mp_quote template class F> struct mp_quote { -#if defined( BOOST_MSVC ) && BOOST_WORKAROUND( BOOST_MSVC, <= 1910 && BOOST_MSVC >= 1900 ) -#else -private: -#endif - - template struct _fn { using type = F; }; - -public: - - // the indirection through _fn works around the language inability + // the indirection through mp_defer works around the language inability // to expand T... into a fixed parameter list of an alias template - template using fn = typename _fn::type; + template using fn = typename mp_defer::type; }; // mp_unquote diff --git a/test/mp_eval_if.cpp b/test/mp_eval_if.cpp index 1c8ed39..7e44c67 100644 --- a/test/mp_eval_if.cpp +++ b/test/mp_eval_if.cpp @@ -1,5 +1,5 @@ -// Copyright 2015 Peter Dimov. +// Copyright 2015, 2017 Peter Dimov. // // Distributed under the Boost Software License, Version 1.0. // @@ -21,19 +21,32 @@ int main() BOOST_TEST_TRAIT_TRUE((std::is_same, mp_identity>)); using boost::mp11::mp_eval_if; + using boost::mp11::mp_eval_if_q; + using boost::mp11::mp_quote; + + using qt_identity = mp_quote; BOOST_TEST_TRAIT_TRUE((std::is_same, char[]>)); BOOST_TEST_TRAIT_TRUE((std::is_same, mp_identity>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char[]>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, mp_identity>)); + using boost::mp11::mp_int; BOOST_TEST_TRAIT_TRUE((std::is_same, char[], mp_identity, void, void, void>, char[]>)); BOOST_TEST_TRAIT_TRUE((std::is_same, char[], mp_identity, void()>, mp_identity>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char[], qt_identity, void, void, void>, char[]>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char[], qt_identity, void()>, mp_identity>)); + using boost::mp11::mp_size_t; BOOST_TEST_TRAIT_TRUE((std::is_same, char[], mp_identity, void, void, void>, char[]>)); BOOST_TEST_TRAIT_TRUE((std::is_same, char[], mp_identity, void()>, mp_identity>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char[], qt_identity, void, void, void>, char[]>)); + BOOST_TEST_TRAIT_TRUE((std::is_same, char[], qt_identity, void()>, mp_identity>)); + return boost::report_errors(); } diff --git a/test/mp_eval_if_sf.cpp b/test/mp_eval_if_sf.cpp index 8eca74e..aed1191 100644 --- a/test/mp_eval_if_sf.cpp +++ b/test/mp_eval_if_sf.cpp @@ -1,5 +1,5 @@ -// Copyright 2017 Peter Dimov. +// Copyright 2017 Peter Dimov. // // Distributed under the Boost Software License, Version 1.0. // @@ -12,14 +12,15 @@ #include using boost::mp11::mp_eval_if; +using boost::mp11::mp_eval_if_q; using boost::mp11::mp_identity_t; +using boost::mp11::mp_valid; +using boost::mp11::mp_quote; template using eval_if = mp_eval_if; int main() { - using boost::mp11::mp_valid; - BOOST_TEST_TRAIT_TRUE((mp_valid)); BOOST_TEST_TRAIT_TRUE((mp_valid)); BOOST_TEST_TRAIT_TRUE((mp_valid)); @@ -30,5 +31,17 @@ int main() BOOST_TEST_TRAIT_FALSE((mp_valid)); BOOST_TEST_TRAIT_FALSE((mp_valid)); + using Qi = mp_quote; + + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + return boost::report_errors(); } From 7100d10c7e359205648f2737f0d2b42c9657daa8 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 15:37:49 +0300 Subject: [PATCH 10/12] Make mp_infoke SFINAE-friendly --- include/boost/mp11/utility.hpp | 15 ++++++++++----- test/Jamfile.v2 | 1 + test/mp_invoke_sf.cpp | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 test/mp_invoke_sf.cpp diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index 1439596..dd85e89 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -116,19 +116,24 @@ template class F> struct mp_quote template using fn = typename mp_defer::type; }; -// mp_unquote +// mp_invoke +#if BOOST_WORKAROUND( BOOST_MSVC, < 1900 ) + namespace detail { -template struct mp_invoke_impl -{ - using type = typename Q::template fn; -}; +template struct mp_invoke_impl: mp_defer {}; } // namespace detail template using mp_invoke = typename detail::mp_invoke_impl::type; +#else + +template using mp_invoke = typename Q::template fn; + +#endif + } // namespace mp11 } // namespace boost diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index f45c9e6..e700170 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -83,6 +83,7 @@ run mp_valid.cpp : : : $(REQ) ; run mp_defer.cpp : : : $(REQ) ; run mp_quote.cpp : : : $(REQ) ; run mp_invoke.cpp : : : $(REQ) ; +run mp_invoke_sf.cpp : : : $(REQ) ; # integer_sequence run integer_sequence.cpp : : : $(REQ) ; diff --git a/test/mp_invoke_sf.cpp b/test/mp_invoke_sf.cpp new file mode 100644 index 0000000..7c2feb6 --- /dev/null +++ b/test/mp_invoke_sf.cpp @@ -0,0 +1,33 @@ + +// Copyright 2015, 2017 Peter Dimov. +// +// Distributed under the Boost Software License, Version 1.0. +// +// See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt + + +#include +#include +#include + +using boost::mp11::mp_invoke; +using boost::mp11::mp_quote; +using boost::mp11::mp_valid; +using boost::mp11::mp_identity_t; + +int main() +{ + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + + using Qi = mp_quote; + + BOOST_TEST_TRAIT_FALSE((mp_valid)); + BOOST_TEST_TRAIT_TRUE((mp_valid)); + BOOST_TEST_TRAIT_FALSE((mp_valid)); + + return boost::report_errors(); +} From 29c4b601b48fce146b2191a7e7d4aea8124c3199 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 15:59:17 +0300 Subject: [PATCH 11/12] Extend msvc-12.0 mp_invoke workaround to g++ 4.x --- include/boost/mp11/utility.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index dd85e89..a1e4472 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -117,7 +117,7 @@ template class F> struct mp_quote }; // mp_invoke -#if BOOST_WORKAROUND( BOOST_MSVC, < 1900 ) +#if BOOST_WORKAROUND( BOOST_MSVC, < 1900 ) || BOOST_WORKAROUND( BOOST_GCC, < 50000 ) namespace detail { From eac1bf28a5332be31e5991c70e2ce41e61736eb6 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 19 May 2017 16:22:41 +0300 Subject: [PATCH 12/12] Unextend mp_invoke g++ 4.x workaround --- include/boost/mp11/utility.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/boost/mp11/utility.hpp b/include/boost/mp11/utility.hpp index a1e4472..35af770 100644 --- a/include/boost/mp11/utility.hpp +++ b/include/boost/mp11/utility.hpp @@ -117,7 +117,7 @@ template class F> struct mp_quote }; // mp_invoke -#if BOOST_WORKAROUND( BOOST_MSVC, < 1900 ) || BOOST_WORKAROUND( BOOST_GCC, < 50000 ) +#if BOOST_WORKAROUND( BOOST_MSVC, < 1900 ) namespace detail { @@ -128,6 +128,10 @@ template struct mp_invoke_impl: mp_defer using mp_invoke = typename detail::mp_invoke_impl::type; +#elif BOOST_WORKAROUND( BOOST_GCC, < 50000 ) + +template using mp_invoke = typename mp_defer::type; + #else template using mp_invoke = typename Q::template fn;