thrust / dependencies /libcudacxx /libcxx /test /std /utilities /tuple /tuple.tuple /tuple.apply /apply.pass.cpp
| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // 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. | |
| // std::array is explicitly allowed to be initialized with A a = { init-list };. | |
| // Disable the missing braces warning for this reason. | |
| 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() | |
| { | |
| 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; | |
| LIBCPP_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)); | |
| LIBCPP_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(int, char**) { | |
| test_constexpr_evaluation(); | |
| test_call_quals_and_arg_types(); | |
| test_return_type(); | |
| test_noexcept(); | |
| return 0; | |
| } | |