diff --git a/doc/mp11/tuple.adoc b/doc/mp11/tuple.adoc
index 53b8c19..b9dd82c 100644
--- a/doc/mp11/tuple.adoc
+++ b/doc/mp11/tuple.adoc
@@ -9,8 +9,17 @@ http://www.boost.org/LICENSE_1_0.txt
[#tuple]
# Tuple Operations,
+:toc:
+:toc-title:
:idprefix:
+## tuple_apply(f, tp)
+
+ template constexpr /*...*/ tuple_apply(F&& f, Tp&& tp);
+
+`tuple_apply(f, tp)` returns `std::forward(f)(std::get(std::forward(tp))...)` for `J` in 0..`N-1`,
+where `N` is `std::tuple_size::type>::value`. Same as `std::apply` in C++17.
+
## tuple_for_each(tp, f)
template constexpr F tuple_for_each(Tp&& tp, F&& f);
diff --git a/include/boost/mp11/tuple.hpp b/include/boost/mp11/tuple.hpp
index 8ac99cc..754295c 100644
--- a/include/boost/mp11/tuple.hpp
+++ b/include/boost/mp11/tuple.hpp
@@ -21,6 +21,26 @@ namespace boost
namespace mp11
{
+// tuple_apply
+namespace detail
+{
+
+template BOOST_CONSTEXPR auto tuple_apply_impl( F && f, Tp && tp, integer_sequence )
+ -> decltype( std::forward(f)( std::get(std::forward(tp))... ) )
+{
+ return std::forward(f)( std::get(std::forward(tp))... );
+}
+
+} // namespace detail
+
+template::type>::value>>
+BOOST_CONSTEXPR auto tuple_apply( F && f, Tp && tp )
+ -> decltype( detail::tuple_apply_impl( std::forward(f), std::forward(tp), Seq() ) )
+{
+ return detail::tuple_apply_impl( std::forward(f), std::forward(tp), Seq() );
+}
+
// tuple_for_each
namespace detail
{
diff --git a/include/boost/mp11_single.hpp b/include/boost/mp11_single.hpp
index af97b45..c4086e2 100644
--- a/include/boost/mp11_single.hpp
+++ b/include/boost/mp11_single.hpp
@@ -2675,6 +2675,26 @@ namespace boost
namespace mp11
{
+// tuple_apply
+namespace detail
+{
+
+template BOOST_CONSTEXPR auto tuple_apply_impl( F && f, Tp && tp, integer_sequence )
+ -> decltype( std::forward(f)( std::get(std::forward(tp))... ) )
+{
+ return std::forward(f)( std::get(std::forward(tp))... );
+}
+
+} // namespace detail
+
+template::type>::value>>
+BOOST_CONSTEXPR auto tuple_apply( F && f, Tp && tp )
+ -> decltype( detail::tuple_apply_impl( std::forward(f), std::forward(tp), Seq() ) )
+{
+ return detail::tuple_apply_impl( std::forward(f), std::forward(tp), Seq() );
+}
+
// tuple_for_each
namespace detail
{
diff --git a/test/Jamfile b/test/Jamfile
index 1521645..02cd885 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -93,9 +93,11 @@ run mp_invoke_sf.cpp : : : $(REQ) ;
# integer_sequence
run integer_sequence.cpp : : : $(REQ) ;
-# tuple_for_each
+# tuple
run tuple_for_each.cpp : : : $(REQ) ;
run tuple_for_each_cx.cpp : : : $(REQ) ;
+run tuple_apply.cpp : : : $(REQ) ;
+run tuple_apply_cx.cpp : : : $(REQ) ;
# set
run mp_set_contains.cpp : : : $(REQ) ;
diff --git a/test/tuple_apply.cpp b/test/tuple_apply.cpp
new file mode 100644
index 0000000..e178dc8
--- /dev/null
+++ b/test/tuple_apply.cpp
@@ -0,0 +1,132 @@
+
+// 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
+#include
+#include
+#include
+
+int main()
+{
+ using boost::mp11::tuple_apply;
+
+ {
+ std::tuple tp{ 1, 2, 3 };
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, tp );
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
+ {
+ std::tuple const tp{ 1, 2, 3 };
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, tp );
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
+#if defined( __clang_major__ ) && __clang_major__ == 3 && __clang_minor__ < 8
+#else
+
+ {
+ std::tuple, std::unique_ptr, std::unique_ptr> tp{ std::unique_ptr(new int(1)), std::unique_ptr(new int(2)), std::unique_ptr(new int(3)) };
+
+ int s = tuple_apply( [&]( std::unique_ptr px, std::unique_ptr py, std::unique_ptr pz ){ return 100 * *px + 10 * *py + *pz; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+#endif
+
+ {
+ std::pair tp{ 1, 2 };
+
+ {
+ int s = tuple_apply( [&]( int x, int y ){ return 10 * x + y; }, tp );
+ BOOST_TEST_EQ( s, 12 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y ){ return 10 * x + y; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 12 );
+ }
+ }
+
+ {
+ std::pair const tp{ 1, 2 };
+
+ {
+ int s = tuple_apply( [&]( int x, int y ){ return 10 * x + y; }, tp );
+ BOOST_TEST_EQ( s, 12 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y ){ return 10 * x + y; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 12 );
+ }
+ }
+
+ {
+ std::array tp{{ 1, 2, 3 }};
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, tp );
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
+ {
+ std::array const tp{{ 1, 2, 3 }};
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, tp );
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = tuple_apply( [&]( int x, int y, int z ){ return 100 * x + 10 * y + z; }, std::move(tp) );
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
+ {
+ std::tuple<> tp;
+
+ BOOST_TEST_EQ( tuple_apply( []{ return 11; }, tp ), 11 );
+ BOOST_TEST_EQ( tuple_apply( []{ return 12; }, std::move( tp ) ), 12 );
+ }
+
+ {
+ std::array tp;
+
+ BOOST_TEST_EQ( tuple_apply( []{ return 11; }, tp ), 11 );
+ BOOST_TEST_EQ( tuple_apply( []{ return 12; }, std::move( tp ) ), 12 );
+ }
+
+ return boost::report_errors();
+}
diff --git a/test/tuple_apply_cx.cpp b/test/tuple_apply_cx.cpp
new file mode 100644
index 0000000..785d5a8
--- /dev/null
+++ b/test/tuple_apply_cx.cpp
@@ -0,0 +1,66 @@
+
+// Copyright 2015 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
+
+// Technically std::tuple isn't constexpr enabled in C++11, but it works with libstdc++
+
+#if defined( BOOST_NO_CXX11_CONSTEXPR ) || ( defined( _LIBCPP_VERSION ) && __cplusplus < 201400L )
+
+int main() {}
+
+#else
+
+#include
+#include
+#include
+
+constexpr int f( int x, int y, int z )
+{
+ return x * 100 + y * 10 + z;
+}
+
+constexpr int g( int x, int y )
+{
+ return x * 10 + y;
+}
+
+constexpr int h()
+{
+ return 11;
+}
+
+int main()
+{
+ {
+ constexpr std::tuple tp{ 1, 2, 3 };
+ constexpr auto r = boost::mp11::tuple_apply( f, tp );
+ static_assert( r == 123, "r == 123" );
+ }
+
+ {
+ constexpr std::pair tp{ 1, 2 };
+ constexpr auto r = boost::mp11::tuple_apply( g, tp );
+ static_assert( r == 12, "r == 12" );
+ }
+
+ {
+ constexpr std::array tp{{ 1, 2, 3 }};
+ constexpr auto r = boost::mp11::tuple_apply( f, tp );
+ static_assert( r == 123, "r == 123" );
+ }
+
+ {
+ constexpr std::tuple<> tp;
+ constexpr auto r = boost::mp11::tuple_apply( h, tp );
+ static_assert( r == 11, "r == 11" );
+ }
+}
+
+#endif
diff --git a/test/tuple_for_each.cpp b/test/tuple_for_each.cpp
index 467d776..c16deb9 100644
--- a/test/tuple_for_each.cpp
+++ b/test/tuple_for_each.cpp
@@ -38,6 +38,26 @@ int main()
}
}
+ {
+ std::tuple const tp{ 1, 2, 3 };
+
+ {
+ int s = 0;
+
+ tuple_for_each( tp, [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = 0;
+
+ tuple_for_each( std::move(tp), [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
#if defined( __clang_major__ ) && __clang_major__ == 3 && __clang_minor__ < 8
#else
@@ -73,6 +93,26 @@ int main()
}
}
+ {
+ std::pair const tp{ 1, 2 };
+
+ {
+ int s = 0;
+
+ tuple_for_each( tp, [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 12 );
+ }
+
+ {
+ int s = 0;
+
+ tuple_for_each( std::move(tp), [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 12 );
+ }
+ }
+
{
std::array tp{{ 1, 2, 3 }};
@@ -93,6 +133,26 @@ int main()
}
}
+ {
+ std::array const tp{{ 1, 2, 3 }};
+
+ {
+ int s = 0;
+
+ tuple_for_each( tp, [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 123 );
+ }
+
+ {
+ int s = 0;
+
+ tuple_for_each( std::move(tp), [&]( int x ){ s = s * 10 + x; } );
+
+ BOOST_TEST_EQ( s, 123 );
+ }
+ }
+
{
std::tuple<> tp;