diff options
Diffstat (limited to 'test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp')
-rw-r--r-- | test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp b/test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp new file mode 100644 index 000000000000..2e821945d09a --- /dev/null +++ b/test/std/utilities/tuple/tuple.tuple/tuple.apply/apply.pass.cpp @@ -0,0 +1,275 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++98, c++03, c++11, c++14 + +// <tuple> + +// template <class F, class T> constexpr decltype(auto) apply(F &&, T &&) + +// Test with different ref/ptr/cv qualified argument types. + +#include <tuple> +#include <array> +#include <utility> +#include <cassert> + +#include "test_macros.h" +#include "type_id.h" + +// std::array is explicitly allowed to be initialized with A a = { init-list };. +// Disable the missing braces warning for this reason. +#include "disable_missing_braces_warning.h" + + +constexpr int constexpr_sum_fn() { return 0; } + +template <class ...Ints> +constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); } + +struct ConstexprSumT { + constexpr ConstexprSumT() = default; + template <class ...Ints> + constexpr int operator()(Ints... values) const { + return constexpr_sum_fn(values...); + } +}; + + +void test_constexpr_evaluation() +{ + constexpr ConstexprSumT sum_obj{}; + { + using Tup = std::tuple<>; + using Fn = int(&)(); + constexpr Tup t; + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 0, ""); + static_assert(std::apply(sum_obj, t) == 0, ""); + } + { + using Tup = std::tuple<int>; + using Fn = int(&)(int); + constexpr Tup t(42); + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 42, ""); + static_assert(std::apply(sum_obj, t) == 42, ""); + } + { + using Tup = std::tuple<int, long>; + using Fn = int(&)(int, int); + constexpr Tup t(42, 101); + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, ""); + static_assert(std::apply(sum_obj, t) == 143, ""); + } + { + using Tup = std::pair<int, long>; + using Fn = int(&)(int, int); + constexpr Tup t(42, 101); + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, ""); + static_assert(std::apply(sum_obj, t) == 143, ""); + } + { + using Tup = std::tuple<int, long, int>; + using Fn = int(&)(int, int, int); + constexpr Tup t(42, 101, -1); + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, ""); + static_assert(std::apply(sum_obj, t) == 142, ""); + } + { + using Tup = std::array<int, 3>; + using Fn = int(&)(int, int, int); + constexpr Tup t = {42, 101, -1}; + static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, ""); + static_assert(std::apply(sum_obj, t) == 142, ""); + } +} + + +enum CallQuals { + CQ_None, + CQ_LValue, + CQ_ConstLValue, + CQ_RValue, + CQ_ConstRValue +}; + +template <class Tuple> +struct CallInfo { + CallQuals quals; + TypeID const* arg_types; + Tuple args; + + template <class ...Args> + CallInfo(CallQuals q, Args&&... xargs) + : quals(q), arg_types(&makeArgumentID<Args&&...>()), args(std::forward<Args>(xargs)...) + {} +}; + +template <class ...Args> +inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))> +makeCallInfo(CallQuals quals, Args&&... args) { + return {quals, std::forward<Args>(args)...}; +} + +struct TrackedCallable { + + TrackedCallable() = default; + + template <class ...Args> auto operator()(Args&&... xargs) & + { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); } + + template <class ...Args> auto operator()(Args&&... xargs) const& + { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); } + + template <class ...Args> auto operator()(Args&&... xargs) && + { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); } + + template <class ...Args> auto operator()(Args&&... xargs) const&& + { return makeCallInfo(CQ_ConstRValue, std::forward<Args>(xargs)...); } +}; + +template <class ...ExpectArgs, class Tuple> +void check_apply_quals_and_types(Tuple&& t) { + TypeID const* const expect_args = &makeArgumentID<ExpectArgs...>(); + TrackedCallable obj; + TrackedCallable const& cobj = obj; + { + auto ret = std::apply(obj, std::forward<Tuple>(t)); + assert(ret.quals == CQ_LValue); + assert(ret.arg_types == expect_args); + assert(ret.args == t); + } + { + auto ret = std::apply(cobj, std::forward<Tuple>(t)); + assert(ret.quals == CQ_ConstLValue); + assert(ret.arg_types == expect_args); + assert(ret.args == t); + } + { + auto ret = std::apply(std::move(obj), std::forward<Tuple>(t)); + assert(ret.quals == CQ_RValue); + assert(ret.arg_types == expect_args); + assert(ret.args == t); + } + { + auto ret = std::apply(std::move(cobj), std::forward<Tuple>(t)); + assert(ret.quals == CQ_ConstRValue); + assert(ret.arg_types == expect_args); + assert(ret.args == t); + } +} + +void test_call_quals_and_arg_types() +{ + TrackedCallable obj; + using Tup = std::tuple<int, int const&, unsigned&&>; + const int x = 42; + unsigned y = 101; + Tup t(-1, x, std::move(y)); + Tup const& ct = t; + check_apply_quals_and_types<int&, int const&, unsigned&>(t); + check_apply_quals_and_types<int const&, int const&, unsigned&>(ct); + check_apply_quals_and_types<int&&, int const&, unsigned&&>(std::move(t)); + check_apply_quals_and_types<int const&&, int const&, unsigned&&>(std::move(ct)); +} + + +struct NothrowMoveable { + NothrowMoveable() noexcept = default; + NothrowMoveable(NothrowMoveable const&) noexcept(false) {} + NothrowMoveable(NothrowMoveable&&) noexcept {} +}; + +template <bool IsNoexcept> +struct TestNoexceptCallable { + template <class ...Args> + NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; } +}; + +void test_noexcept() +{ + TestNoexceptCallable<true> nec; + TestNoexceptCallable<false> tc; + { + // test that the functions noexcept-ness is propagated + using Tup = std::tuple<int, const char*, long>; + Tup t; + ASSERT_NOEXCEPT(std::apply(nec, t)); + ASSERT_NOT_NOEXCEPT(std::apply(tc, t)); + } + { + // test that the noexcept-ness of the argument conversions is checked. + using Tup = std::tuple<NothrowMoveable, int>; + Tup t; + ASSERT_NOT_NOEXCEPT(std::apply(nec, t)); + ASSERT_NOEXCEPT(std::apply(nec, std::move(t))); + } +} + +namespace ReturnTypeTest { + static int my_int = 42; + + template <int N> struct index {}; + + void f(index<0>) {} + + int f(index<1>) { return 0; } + + int & f(index<2>) { return static_cast<int &>(my_int); } + int const & f(index<3>) { return static_cast<int const &>(my_int); } + int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); } + int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); } + + int && f(index<6>) { return static_cast<int &&>(my_int); } + int const && f(index<7>) { return static_cast<int const &&>(my_int); } + int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); } + int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); } + + int * f(index<10>) { return static_cast<int *>(&my_int); } + int const * f(index<11>) { return static_cast<int const *>(&my_int); } + int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); } + int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); } + + template <int Func, class Expect> + void test() + { + using RawInvokeResult = decltype(f(index<Func>{})); + static_assert(std::is_same<RawInvokeResult, Expect>::value, ""); + using FnType = RawInvokeResult (*) (index<Func>); + FnType fn = f; + std::tuple<index<Func>> t; ((void)t); + using InvokeResult = decltype(std::apply(fn, t)); + static_assert(std::is_same<InvokeResult, Expect>::value, ""); + } +} // end namespace ReturnTypeTest + +void test_return_type() +{ + using ReturnTypeTest::test; + test<0, void>(); + test<1, int>(); + test<2, int &>(); + test<3, int const &>(); + test<4, int volatile &>(); + test<5, int const volatile &>(); + test<6, int &&>(); + test<7, int const &&>(); + test<8, int volatile &&>(); + test<9, int const volatile &&>(); + test<10, int *>(); + test<11, int const *>(); + test<12, int volatile *>(); + test<13, int const volatile *>(); +} + +int main() { + test_constexpr_evaluation(); + test_call_quals_and_arg_types(); + test_return_type(); + test_noexcept(); +} |