From 2e6c2abcde8a010b1ae6991b45c6c294f50e9e75 Mon Sep 17 00:00:00 2001 From: slymz Date: Mon, 4 May 2020 17:35:31 -0400 Subject: [PATCH 1/4] Updated docs "Computing Return Types" - Suggests `std::invoke_result` as an in place modern alternative for `Qret`. - Fixes a bug with `const` variant types using `std::decay_t` instead of `std::remove_reference_t`. --- doc/mp11/examples.adoc | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/mp11/examples.adoc b/doc/mp11/examples.adoc index 858bd55..61dac8a 100644 --- a/doc/mp11/examples.adoc +++ b/doc/mp11/examples.adoc @@ -410,21 +410,28 @@ We'll first define a helper quoted metafunction `Qret` that returns the resul decltype( std::declval()( std::declval()... ) ); }; -(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. +But we can make use of another C++17 feature for an alternative which can be defined inside and simplifies the implementation further: + + using Qret_F = mp_bind_front; 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_q, std::remove_reference_t...>; + using R = mp_product_q, std::decay_t...>; + // or + using R = mp_product_q...>; Why does this work? `mp_product, L2, ..., Ln>` returns `L1, ...>`, 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`. (`mp_product_q` is -the same as `mp_product`, but for quoted metafunctions such as our `Qret`.) +the same as `mp_product`, but for quoted metafunctions such as our `Qret` or `Qret_F`.) We needed to use `std::decay_t` for precisely the +same reason as in the link:#fixing-tuple_cat[Fixing tuple_cat example], where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. + One more step remains. Suppose that, as above, we're passing two variants of type `std::variant` 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, we pass the result to `mp_unique`: - using R = mp_unique, std::remove_reference_t...>>; + using R = mp_unique...>>; and we're done: @@ -438,15 +445,11 @@ and we're done: using namespace boost::mp11; -template struct Qret -{ - template using fn = - decltype( std::declval()( std::declval()... ) ); -}; template auto rvisit( F&& f, V&&... v ) { - using R = mp_unique, std::remove_reference_t...>>; + using Qret_F = mp_bind_front; + using R = mp_unique...>>; return std::visit( [&]( auto&&... x ) { return R( std::forward(f)( std::forward(x)... ) ); }, From 742980e5331bc0225ef738f5b34c0bb7b77a736d Mon Sep 17 00:00:00 2001 From: slymz Date: Wed, 6 May 2020 18:27:35 -0400 Subject: [PATCH 2/4] for std::decay fix, anchor and cross-reference tuple-cat fix more accurately --- doc/mp11/examples.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/mp11/examples.adoc b/doc/mp11/examples.adoc index 61dac8a..314a9ff 100644 --- a/doc/mp11/examples.adoc +++ b/doc/mp11/examples.adoc @@ -264,6 +264,7 @@ Long story short, we need `std::move(tp)` in `tuple_cat_` to make `tp` an rvalue return R{ std::get(std::get(std::move(tp)))... }; } +[[fixing-tuple-cat-const-issue]] Next, `const`-qualified tuples. The issue here is that we're stripping references from the input tuples, but not `const`. As a result, we're trying to manipulate types such as `tuple const` with Mp11 algorithms, and these types do not fit the list concept. We just need to strip qualifiers as well, by defining the useful `remove_cv_ref` @@ -424,7 +425,7 @@ With `Qret` in hand, a `variant` of the possible return types is just a matter o Why does this work? `mp_product, L2, ..., Ln>` returns `L1, ...>`, 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`. (`mp_product_q` is the same as `mp_product`, but for quoted metafunctions such as our `Qret` or `Qret_F`.) We needed to use `std::decay_t` for precisely the -same reason as in the link:#fixing-tuple_cat[Fixing tuple_cat example], where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. +same reason as in the <>, where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. One more step remains. Suppose that, as above, we're passing two variants of type `std::variant` and `F` is From 35a86da1786f5dc9edbfb56c9ec354f465b82582 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 23 May 2020 21:48:41 +0300 Subject: [PATCH 3/4] Revert "for std::decay fix, anchor and cross-reference tuple-cat fix more accurately" This reverts commit 742980e5331bc0225ef738f5b34c0bb7b77a736d. --- doc/mp11/examples.adoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/mp11/examples.adoc b/doc/mp11/examples.adoc index 314a9ff..61dac8a 100644 --- a/doc/mp11/examples.adoc +++ b/doc/mp11/examples.adoc @@ -264,7 +264,6 @@ Long story short, we need `std::move(tp)` in `tuple_cat_` to make `tp` an rvalue return R{ std::get(std::get(std::move(tp)))... }; } -[[fixing-tuple-cat-const-issue]] Next, `const`-qualified tuples. The issue here is that we're stripping references from the input tuples, but not `const`. As a result, we're trying to manipulate types such as `tuple const` with Mp11 algorithms, and these types do not fit the list concept. We just need to strip qualifiers as well, by defining the useful `remove_cv_ref` @@ -425,7 +424,7 @@ With `Qret` in hand, a `variant` of the possible return types is just a matter o Why does this work? `mp_product, L2, ..., Ln>` returns `L1, ...>`, 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`. (`mp_product_q` is the same as `mp_product`, but for quoted metafunctions such as our `Qret` or `Qret_F`.) We needed to use `std::decay_t` for precisely the -same reason as in the <>, where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. +same reason as in the link:#fixing-tuple_cat[Fixing tuple_cat example], where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. One more step remains. Suppose that, as above, we're passing two variants of type `std::variant` and `F` is From 3427d716c6d3d6a831558a795fa6932d860ff702 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Sat, 23 May 2020 22:05:51 +0300 Subject: [PATCH 4/4] Update examples.adoc --- doc/mp11/examples.adoc | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/doc/mp11/examples.adoc b/doc/mp11/examples.adoc index 61dac8a..176e8c3 100644 --- a/doc/mp11/examples.adoc +++ b/doc/mp11/examples.adoc @@ -370,7 +370,7 @@ template` that returns the resul decltype( std::declval()( std::declval()... ) ); }; -Unfortunately, we can't just define this metafunction inside `rvisit`; the language prohibits defining template aliases inside functions. -But we can make use of another C++17 feature for an alternative which can be defined inside and simplifies the implementation further: +It turns out that {cpp}17 already contains a metafunction that returns the result of the application of a function `F` to arguments +of type `T...`: `std::invoke_result_t`. We can make use of it to simplify our `Qret` to - using Qret_F = mp_bind_front; + template struct Qret + { + template using fn = std::invoke_result_t; + }; + +which in Mp11 can be expressed more concisely as + + using Qret = mp_bind_front; 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_q, std::decay_t...>; - // or - using R = mp_product_q...>; + using R = mp_product_q...>; Why does this work? `mp_product, L2, ..., Ln>` returns `L1, ...>`, 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`. (`mp_product_q` is -the same as `mp_product`, but for quoted metafunctions such as our `Qret` or `Qret_F`.) We needed to use `std::decay_t` for precisely the -same reason as in the link:#fixing-tuple_cat[Fixing tuple_cat example], where `std::decay_t` is an equivalent alternative to `remove_cv_ref`. - +the same as `mp_product`, but for quoted metafunctions such as our `Qret`.) One more step remains. Suppose that, as above, we're passing two variants of type `std::variant` 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, we pass the result to `mp_unique`: - using R = mp_unique...>>; + using R = mp_unique...>>; and we're done: @@ -445,11 +448,14 @@ and we're done: using namespace boost::mp11; +template using remove_cv_ref = typename std::remove_cv< + typename std::remove_reference::type>::type; template auto rvisit( F&& f, V&&... v ) { - using Qret_F = mp_bind_front; - using R = mp_unique...>>; + using Qret = mp_bind_front; + + using R = mp_unique...>>; return std::visit( [&]( auto&&... x ) { return R( std::forward(f)( std::forward(x)... ) ); }, @@ -475,7 +481,7 @@ int main() print_variant( "v1", v1 ); - std::variant v2( 3.14 ); + std::variant const v2( 3.14 ); print_variant( "v2", v2 );