1
0
forked from boostorg/mp11

Squash-merge PR #50

This commit is contained in:
Peter Dimov
2020-05-23 19:16:07 +03:00
parent b07eb08a2f
commit dcf0d7ebdf
6 changed files with 357 additions and 0 deletions

View File

@ -13,6 +13,7 @@ http://www.boost.org/LICENSE_1_0.txt
## Changes in 1.74.0
* Improve compilation performance of `mp_with_index<N>` for large `N`
* Added `tuple_transform` (contributed by Hans Dembinski)
## Changes in 1.73.0

View File

@ -36,3 +36,15 @@ The name of the function doesn't match the {cpp}17 one to avoid ambiguities when
expression `f(std::get<J>(std::forward<Tp>(tp)))` for `J` in 0..`N-1`, where `N` is `std::tuple_size<typename std::remove_reference<Tp>::type>::value`.
Returns `std::forward<F>(f)`.
## tuple_transform(f, tp)
template<class F, class... Tps> constexpr /*...*/ tuple_transform(F const& f, Tps const&... tps);
`tuple_transform(f, tps...)` 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<J>(tps)...)` for `J` in 0..`N-1`, where `N` is
`std::tuple_size<Tp>::value` for the first tuple `Tp`. The results are returned as a `Tp<Ts...>` 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.

View File

@ -86,6 +86,83 @@ template<class Tp, class F> BOOST_MP11_CONSTEXPR F tuple_for_each( Tp && tp, F &
return detail::tuple_for_each_impl( std::forward<Tp>(tp), seq(), std::forward<F>(f) );
}
// tuple_transform
namespace detail
{
template <class Tp, class F, class... Tps>
struct tuple_transform_caller {
F const& f_;
std::tuple<Tp const&, Tps const&...> const tps_;
BOOST_MP11_CONSTEXPR tuple_transform_caller( Tp const& tp, F const& f, Tps const&... tps )
: f_{f}, tps_{tp, tps...} {}
template <std::size_t J, std::size_t... I>
BOOST_MP11_CONSTEXPR auto operator()(
std::integral_constant<std::size_t, J>,
integer_sequence<std::size_t, I...>) const
-> decltype(f_(std::get<J>(std::get<I>(tps_))...))
{
return f_(std::get<J>(std::get<I>(tps_))...);
}
};
template <class Tp, class F>
struct tuple_transform_caller<Tp, F> {
F const& f_;
Tp const& tp_;
BOOST_MP11_CONSTEXPR tuple_transform_caller( Tp const& tp, F const& f )
: f_{f}, tp_{tp} {}
template <std::size_t J>
BOOST_MP11_CONSTEXPR auto operator()(
std::integral_constant<std::size_t, J>,
integer_sequence<std::size_t, 0>
) const
-> decltype(f_(std::get<J>(tp_)))
{
return f_(std::get<J>(tp_));
}
};
template<
template <class...> class Tp,
class... Ts,
class... Us,
std::size_t... J,
class Seq = make_index_sequence<sizeof...(Us)>, // tuple sequence
class R = Tp<decltype(std::declval<tuple_transform_caller<Tp<Ts...>, Us...>>()(
std::integral_constant<std::size_t, J>{}, Seq{}
))...>
>
BOOST_MP11_CONSTEXPR R tuple_transform_impl(
tuple_transform_caller<Tp<Ts...>, Us...> c,
integer_sequence<std::size_t, J...>)
{
return R( c(std::integral_constant<std::size_t, J>{}, Seq{})... );
}
} // namespace detail
// warning: evaluation order is undefined
template<
class F,
class Tp,
class... Tps,
class Seq = make_index_sequence<std::tuple_size<Tp>::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<detail::tuple_transform_caller<Tp, F, Tps...>>(), Seq{}
))
{
return detail::tuple_transform_impl(
detail::tuple_transform_caller<Tp, F, Tps...>{ tp, f, tps... }, Seq{}
);
}
} // namespace mp11
} // namespace boost

View File

@ -156,6 +156,8 @@ run tuple_apply.cpp ;
compile tuple_apply_cx.cpp ;
run construct_from_tuple.cpp ;
compile construct_from_tuple_cx.cpp ;
run tuple_transform.cpp ;
compile tuple_transform_cx.cpp ;
# set
run mp_set_contains.cpp ;

190
test/tuple_transform.cpp Normal file
View File

@ -0,0 +1,190 @@
// Copyright 2020 Hans Dembinski.
//
// 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 <boost/mp11/tuple.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <tuple>
#include <utility>
#include <iosfwd>
// family of test types with state
template <int N>
struct T {
int value;
T() : value{N} {};
explicit T(int n) : value{n} {}
};
template <int N>
std::ostream& operator<<( std::ostream& os, T<N> const& t )
{
os << t.value;
return os;
}
// test function changes type and value
struct F {
template<int N, int M=1> T<N+M> operator()( T<N> a, T<M> b={} ) const
{
return T<N+M>{a.value + b.value + 1};
}
};
int main()
{
using boost::mp11::tuple_transform;
{
std::tuple<T<1>, T<2>, T<3>> tp;
std::tuple<T<4>, T<5>, T<6>> tp2;
{
std::tuple<T<2>, T<3>, T<4>> s = tuple_transform( F{}, tp );
BOOST_TEST_EQ( std::get<0>(s).value, 3 );
BOOST_TEST_EQ( std::get<1>(s).value, 4 );
BOOST_TEST_EQ( std::get<2>(s).value, 5 );
}
{
std::tuple<T<2>, T<3>, T<4>> 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 );
BOOST_TEST_EQ( std::get<2>(s).value, 5 );
}
{
std::tuple<T<5>, T<7>, T<9>> s = tuple_transform( F{}, tp, tp2 );
BOOST_TEST_EQ( std::get<0>(s).value, 6 );
BOOST_TEST_EQ( std::get<1>(s).value, 8 );
BOOST_TEST_EQ( std::get<2>(s).value, 10 );
}
{
std::tuple<T<5>, T<7>, T<9>> s = tuple_transform(
F{}, std::move(tp), std::move(tp2)
);
BOOST_TEST_EQ( std::get<0>(s).value, 6 );
BOOST_TEST_EQ( std::get<1>(s).value, 8 );
BOOST_TEST_EQ( std::get<2>(s).value, 10 );
}
}
{
std::tuple<T<1>, T<2>, T<3>> const tp;
std::tuple<T<4>, T<5>, T<6>> const tp2;
{
std::tuple<T<2>, T<3>, T<4>> s = tuple_transform( F{}, tp );
BOOST_TEST_EQ( std::get<0>(s).value, 3 );
BOOST_TEST_EQ( std::get<1>(s).value, 4 );
BOOST_TEST_EQ( std::get<2>(s).value, 5 );
}
{
std::tuple<T<2>, T<3>, T<4>> 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 );
BOOST_TEST_EQ( std::get<2>(s).value, 5 );
}
{
std::tuple<T<5>, T<7>, T<9>> s = tuple_transform( F{}, tp, tp2 );
BOOST_TEST_EQ( std::get<0>(s).value, 6 );
BOOST_TEST_EQ( std::get<1>(s).value, 8 );
BOOST_TEST_EQ( std::get<2>(s).value, 10 );
}
{
std::tuple<T<5>, T<7>, T<9>> s = tuple_transform(
F{}, std::move(tp), std::move(tp2)
);
BOOST_TEST_EQ( std::get<0>(s).value, 6 );
BOOST_TEST_EQ( std::get<1>(s).value, 8 );
BOOST_TEST_EQ( std::get<2>(s).value, 10 );
}
}
{
std::pair<T<1>, T<2>> tp;
std::pair<T<3>, T<4>> tp2;
{
std::pair<T<2>, 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<2>, 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<4>, 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<4>, T<6>> s = tuple_transform(
F{}, std::move(tp), std::move(tp2)
);
BOOST_TEST_EQ( std::get<0>(s).value, 5 );
BOOST_TEST_EQ( std::get<1>(s).value, 7 );
}
}
{
std::pair<T<1>, T<2>> const tp;
std::pair<T<3>, T<4>> const tp2;
{
std::pair<T<2>, 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<2>, 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<4>, 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<4>, T<6>> s = tuple_transform(
F{}, std::move(tp), std::move(tp2)
);
BOOST_TEST_EQ( std::get<0>(s).value, 5 );
BOOST_TEST_EQ( std::get<1>(s).value, 7 );
}
}
{
std::tuple<> tp;
{
auto s = tuple_transform( F{}, tp );
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(s), std::tuple<>>));
}
{
auto s = tuple_transform( F{}, std::move(tp) );
BOOST_TEST_TRAIT_TRUE((std::is_same<decltype(s), std::tuple<>>));
}
}
return boost::report_errors();
}

View File

@ -0,0 +1,75 @@
// Copyright 2020 Hans Dembinski.
//
// 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 <boost/mp11/tuple.hpp>
#include <boost/mp11/detail/config.hpp>
// Technically std::tuple isn't constexpr enabled in C++11, but it works with libstdc++
#if defined( BOOST_MP11_NO_CONSTEXPR ) || ( !defined( __GLIBCXX__ ) && __cplusplus < 201400L )
int main() {}
#else
#include <tuple>
#include <type_traits>
// family of test types with state
template <int N>
struct T {
int value;
constexpr T() : value{N} {};
constexpr explicit T(int n) : value{n} {}
};
struct F
{
template<int N, int M=1> constexpr T<N+M> operator()( T<N> a, T<M> b={} ) const
{
return T<N+M>{a.value + b.value + 1};
}
};
int main()
{
{
constexpr std::tuple<T<1>, T<2>> tp;
constexpr std::tuple<T<3>, T<4>> tp2;
{
constexpr std::tuple<T<2>, T<3>> r = boost::mp11::tuple_transform(
F{}, tp
);
static_assert(std::get<0>(r).value == 3, "get<0>(r).value == 3" );
static_assert(std::get<1>(r).value == 4, "get<1>(r).value == 4" );
}
{
constexpr std::tuple<T<4>, T<6>> r = boost::mp11::tuple_transform(
F{}, tp, tp2
);
static_assert(std::get<0>(r).value == 5, "get<0>(r).value == 5" );
static_assert(std::get<1>(r).value == 7, "get<1>(r).value == 7" );
}
}
#if defined( __clang_major__ ) && __clang_major__ == 3 && __clang_minor__ < 9
// "error: default initialization of an object of const type 'const std::tuple<>' without a user-provided default constructor"
#else
{
constexpr std::tuple<> tp;
constexpr std::tuple<> r = boost::mp11::tuple_transform( F{}, tp );
(void)r;
}
#endif
}
#endif