diff --git a/doc/mp11/tuple.adoc b/doc/mp11/tuple.adoc index 25da1fd..b89a73b 100644 --- a/doc/mp11/tuple.adoc +++ b/doc/mp11/tuple.adoc @@ -37,14 +37,14 @@ expression `f(std::get(std::forward(tp)))` for `J` in 0..`N-1`, where `N` Returns `std::forward(f)`. -## tuple_transform(f, tp) +## tuple_transform(f, tp...) - template constexpr /*...*/ tuple_transform(F const& f, Tps const&... tps); + template constexpr /*...*/ tuple_transform(F const& f, Tp&&... tp); -`tuple_transform(f, tps...)` accepts a function object `f` followed by one or more tuples of equal length +`tuple_transform(f, tp...)` accepts a function object `f` followed by one or more tuples of equal length (`std::pair` is also accepted). The callable `f` must accept as many arguments as there are tuples. The function object is called with the first elements of each tuple, then with the second element of each tuple, -and so on, by evaluating the expression `f(std::get(tps)...)` for `J` in 0..`N-1`, where `N` is -`std::tuple_size::value` for the first tuple `Tp`. The results are returned as a `Tp` with `Ts...` -deduced from the return values of `f`. Warning: The order in which the elements of the tuples are processed -is undefined. Calling `f` should not have side-effects. +and so on, by evaluating the expression `f(std::get(std::forward(tp))...)` for `J` in 0..`N-1`, where +`N` is the length of the tuples. The results are returned as a `std::tuple` with `T...` deduced from the +return values of `f`. The order in which the elements of the tuples are processed is unspecified. Calling `f` +should not have side effects. diff --git a/include/boost/mp11/tuple.hpp b/include/boost/mp11/tuple.hpp index 872f70f..94f5456 100644 --- a/include/boost/mp11/tuple.hpp +++ b/include/boost/mp11/tuple.hpp @@ -1,7 +1,7 @@ #ifndef BOOST_MP11_TUPLE_HPP_INCLUDED #define BOOST_MP11_TUPLE_HPP_INCLUDED -// Copyright 2015, 2017 Peter Dimov. +// Copyright 2015-2020 Peter Dimov. // // Distributed under the Boost Software License, Version 1.0. // @@ -90,77 +90,33 @@ template BOOST_MP11_CONSTEXPR F tuple_for_each( Tp && tp, F & namespace detail { -template -struct tuple_transform_caller { - F const& f_; - std::tuple const tps_; - - BOOST_MP11_CONSTEXPR tuple_transform_caller( Tp const& tp, F const& f, Tps const&... tps ) - : f_{f}, tps_{tp, tps...} {} - - template - BOOST_MP11_CONSTEXPR auto operator()( - std::integral_constant, - integer_sequence) const - -> decltype(f_(std::get(std::get(tps_))...)) - { - return f_(std::get(std::get(tps_))...); - } -}; - -template -struct tuple_transform_caller { - F const& f_; - Tp const& tp_; - - BOOST_MP11_CONSTEXPR tuple_transform_caller( Tp const& tp, F const& f ) - : f_{f}, tp_{tp} {} - - template - BOOST_MP11_CONSTEXPR auto operator()( - std::integral_constant, - integer_sequence - ) const - -> decltype(f_(std::get(tp_))) - { - return f_(std::get(tp_)); - } -}; - -template< - template class Tp, - class... Ts, - class... Us, - std::size_t... J, - class Seq = make_index_sequence, // tuple sequence - class R = Tp, Us...>>()( - std::integral_constant{}, Seq{} - ))...> -> -BOOST_MP11_CONSTEXPR R tuple_transform_impl( - tuple_transform_caller, Us...> c, - integer_sequence) +template +BOOST_MP11_CONSTEXPR auto tp_extract( Tp&&... tp ) + -> decltype( std::forward_as_tuple( std::get( std::forward( tp ) )... ) ) { - return R( c(std::integral_constant{}, Seq{})... ); + return std::forward_as_tuple( std::get( std::forward( tp ) )... ); +} + +template BOOST_MP11_CONSTEXPR auto tp_forward( T&&... t ) -> std::tuple +{ + return std::tuple( std::forward( t )... ); +} + +template +BOOST_MP11_CONSTEXPR auto tuple_transform_impl( integer_sequence, F const& f, Tp&&... tp ) + -> decltype( tp_forward( tuple_apply( f, tp_extract( std::forward(tp)... ) )... ) ) +{ + return tp_forward( tuple_apply( f, tp_extract( std::forward(tp)... ) )... ); } } // namespace detail -// warning: evaluation order is undefined -template< - class F, - class Tp, - class... Tps, - class Seq = make_index_sequence::value> // element sequence -> -BOOST_MP11_CONSTEXPR auto tuple_transform( F const& f, Tp const& tp, Tps const&... tps ) - -> decltype(detail::tuple_transform_impl( - std::declval>(), Seq{} - )) +template::type>::value>> +BOOST_MP11_CONSTEXPR auto tuple_transform( F const& f, Tp1&& tp1, Tp&&... tp ) + -> decltype( detail::tuple_transform_impl( Seq(), f, std::forward(tp1), std::forward(tp)... ) ) { - return detail::tuple_transform_impl( - detail::tuple_transform_caller{ tp, f, tps... }, Seq{} - ); + return detail::tuple_transform_impl( Seq(), f, std::forward(tp1), std::forward(tp)... ); } } // namespace mp11 diff --git a/test/tuple_transform.cpp b/test/tuple_transform.cpp index d7d0b79..22ed0ea 100644 --- a/test/tuple_transform.cpp +++ b/test/tuple_transform.cpp @@ -115,25 +115,25 @@ int main() std::pair, T<4>> tp2; { - std::pair, T<3>> s = tuple_transform( F{}, tp ); + std::tuple, T<3>> s = tuple_transform( F{}, tp ); BOOST_TEST_EQ( std::get<0>(s).value, 3 ); BOOST_TEST_EQ( std::get<1>(s).value, 4 ); } { - std::pair, T<3>> s = tuple_transform( F{}, std::move(tp) ); + std::tuple, T<3>> s = tuple_transform( F{}, std::move(tp) ); BOOST_TEST_EQ( std::get<0>(s).value, 3 ); BOOST_TEST_EQ( std::get<1>(s).value, 4 ); } { - std::pair, T<6>> s = tuple_transform( F{}, tp, tp2 ); + std::tuple, T<6>> s = tuple_transform( F{}, tp, tp2 ); BOOST_TEST_EQ( std::get<0>(s).value, 5 ); BOOST_TEST_EQ( std::get<1>(s).value, 7 ); } { - std::pair, T<6>> s = tuple_transform( + std::tuple, T<6>> s = tuple_transform( F{}, std::move(tp), std::move(tp2) ); BOOST_TEST_EQ( std::get<0>(s).value, 5 ); @@ -146,25 +146,25 @@ int main() std::pair, T<4>> const tp2; { - std::pair, T<3>> s = tuple_transform( F{}, tp ); + std::tuple, T<3>> s = tuple_transform( F{}, tp ); BOOST_TEST_EQ( std::get<0>(s).value, 3 ); BOOST_TEST_EQ( std::get<1>(s).value, 4 ); } { - std::pair, T<3>> s = tuple_transform( F{}, std::move(tp) ); + std::tuple, T<3>> s = tuple_transform( F{}, std::move(tp) ); BOOST_TEST_EQ( std::get<0>(s).value, 3 ); BOOST_TEST_EQ( std::get<1>(s).value, 4 ); } { - std::pair, T<6>> s = tuple_transform( F{}, tp, tp2 ); + std::tuple, T<6>> s = tuple_transform( F{}, tp, tp2 ); BOOST_TEST_EQ( std::get<0>(s).value, 5 ); BOOST_TEST_EQ( std::get<1>(s).value, 7 ); } { - std::pair, T<6>> s = tuple_transform( + std::tuple, T<6>> s = tuple_transform( F{}, std::move(tp), std::move(tp2) ); BOOST_TEST_EQ( std::get<0>(s).value, 5 );