279 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410 11// <tuple>12 13// template <class F, class T> constexpr decltype(auto) apply(F &&, T &&) noexcept(see below) // noexcept since C++2314 15// Test with different ref/ptr/cv qualified argument types.16 17#include <tuple>18#include <array>19#include <utility>20#include <cassert>21 22#include "test_macros.h"23#include "type_id.h"24 25constexpr int constexpr_sum_fn() { return 0; }26 27template <class ...Ints>28constexpr int constexpr_sum_fn(int x1, Ints... rest) { return x1 + constexpr_sum_fn(rest...); }29 30struct ConstexprSumT {31 constexpr ConstexprSumT() = default;32 template <class ...Ints>33 constexpr int operator()(Ints... values) const {34 return constexpr_sum_fn(values...);35 }36};37 38 39void test_constexpr_evaluation()40{41 constexpr ConstexprSumT sum_obj{};42 {43 using Tup = std::tuple<>;44 using Fn = int(&)();45 constexpr Tup t;46 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 0, "");47 static_assert(std::apply(sum_obj, t) == 0, "");48 }49 {50 using Tup = std::tuple<int>;51 using Fn = int(&)(int);52 constexpr Tup t(42);53 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 42, "");54 static_assert(std::apply(sum_obj, t) == 42, "");55 }56 {57 using Tup = std::tuple<int, long>;58 using Fn = int(&)(int, int);59 constexpr Tup t(42, 101);60 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");61 static_assert(std::apply(sum_obj, t) == 143, "");62 }63 {64 using Tup = std::pair<int, long>;65 using Fn = int(&)(int, int);66 constexpr Tup t(42, 101);67 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 143, "");68 static_assert(std::apply(sum_obj, t) == 143, "");69 }70 {71 using Tup = std::tuple<int, long, int>;72 using Fn = int(&)(int, int, int);73 constexpr Tup t(42, 101, -1);74 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");75 static_assert(std::apply(sum_obj, t) == 142, "");76 }77 {78 using Tup = std::array<int, 3>;79 using Fn = int(&)(int, int, int);80 constexpr Tup t = {42, 101, -1};81 static_assert(std::apply(static_cast<Fn>(constexpr_sum_fn), t) == 142, "");82 static_assert(std::apply(sum_obj, t) == 142, "");83 }84}85 86 87enum CallQuals {88 CQ_None,89 CQ_LValue,90 CQ_ConstLValue,91 CQ_RValue,92 CQ_ConstRValue93};94 95template <class Tuple>96struct CallInfo {97 CallQuals quals;98 TypeID const* arg_types;99 Tuple args;100 101 template <class ...Args>102 CallInfo(CallQuals q, Args&&... xargs)103 : quals(q), arg_types(&makeArgumentID<Args&&...>()), args(std::forward<Args>(xargs)...)104 {}105};106 107template <class ...Args>108inline CallInfo<decltype(std::forward_as_tuple(std::declval<Args>()...))>109makeCallInfo(CallQuals quals, Args&&... args) {110 return {quals, std::forward<Args>(args)...};111}112 113struct TrackedCallable {114 115 TrackedCallable() = default;116 117 template <class ...Args> auto operator()(Args&&... xargs) &118 { return makeCallInfo(CQ_LValue, std::forward<Args>(xargs)...); }119 120 template <class ...Args> auto operator()(Args&&... xargs) const&121 { return makeCallInfo(CQ_ConstLValue, std::forward<Args>(xargs)...); }122 123 template <class ...Args> auto operator()(Args&&... xargs) &&124 { return makeCallInfo(CQ_RValue, std::forward<Args>(xargs)...); }125 126 template <class ...Args> auto operator()(Args&&... xargs) const&&127 { return makeCallInfo(CQ_ConstRValue, std::forward<Args>(xargs)...); }128};129 130template <class ...ExpectArgs, class Tuple>131void check_apply_quals_and_types(Tuple&& t) {132 TypeID const* const expect_args = &makeArgumentID<ExpectArgs...>();133 TrackedCallable obj;134 TrackedCallable const& cobj = obj;135 {136 auto ret = std::apply(obj, std::forward<Tuple>(t));137 assert(ret.quals == CQ_LValue);138 assert(ret.arg_types == expect_args);139 assert(ret.args == t);140 }141 {142 auto ret = std::apply(cobj, std::forward<Tuple>(t));143 assert(ret.quals == CQ_ConstLValue);144 assert(ret.arg_types == expect_args);145 assert(ret.args == t);146 }147 {148 auto ret = std::apply(std::move(obj), std::forward<Tuple>(t));149 assert(ret.quals == CQ_RValue);150 assert(ret.arg_types == expect_args);151 assert(ret.args == t);152 }153 {154 auto ret = std::apply(std::move(cobj), std::forward<Tuple>(t));155 assert(ret.quals == CQ_ConstRValue);156 assert(ret.arg_types == expect_args);157 assert(ret.args == t);158 }159}160 161void test_call_quals_and_arg_types()162{163 using Tup = std::tuple<int, int const&, unsigned&&>;164 const int x = 42;165 unsigned y = 101;166 Tup t(-1, x, std::move(y));167 Tup const& ct = t;168 check_apply_quals_and_types<int&, int const&, unsigned&>(t);169 check_apply_quals_and_types<int const&, int const&, unsigned&>(ct);170 check_apply_quals_and_types<int&&, int const&, unsigned&&>(std::move(t));171 check_apply_quals_and_types<int const&&, int const&, unsigned&&>(std::move(ct));172}173 174 175struct NothrowMoveable {176 NothrowMoveable() noexcept = default;177 NothrowMoveable(NothrowMoveable const&) noexcept(false) {}178 NothrowMoveable(NothrowMoveable&&) noexcept {}179};180 181template <bool IsNoexcept>182struct TestNoexceptCallable {183 template <class ...Args>184 NothrowMoveable operator()(Args...) const noexcept(IsNoexcept) { return {}; }185};186 187void test_noexcept()188{189 TestNoexceptCallable<true> nec;190 TestNoexceptCallable<false> tc;191 {192 // test that the functions noexcept-ness is propagated193 using Tup = std::tuple<int, const char*, long>;194 Tup t;195#if TEST_STD_VER >= 23196 ASSERT_NOEXCEPT(std::apply(nec, t));197#else198 LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, t));199#endif200 ASSERT_NOT_NOEXCEPT(std::apply(tc, t));201 }202 {203 // test that the noexcept-ness of the argument conversions is checked.204 using Tup = std::tuple<NothrowMoveable, int>;205 Tup t;206 ASSERT_NOT_NOEXCEPT(std::apply(nec, t));207#if TEST_STD_VER >= 23208 ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));209#else210 LIBCPP_ASSERT_NOEXCEPT(std::apply(nec, std::move(t)));211#endif212 }213}214 215namespace ReturnTypeTest {216 static int my_int = 42;217 218 template <int N> struct index {};219 220 void f(index<0>) {}221 222 int f(index<1>) { return 0; }223 224 int & f(index<2>) { return static_cast<int &>(my_int); }225 int const & f(index<3>) { return static_cast<int const &>(my_int); }226 int volatile & f(index<4>) { return static_cast<int volatile &>(my_int); }227 int const volatile & f(index<5>) { return static_cast<int const volatile &>(my_int); }228 229 int && f(index<6>) { return static_cast<int &&>(my_int); }230 int const && f(index<7>) { return static_cast<int const &&>(my_int); }231 int volatile && f(index<8>) { return static_cast<int volatile &&>(my_int); }232 int const volatile && f(index<9>) { return static_cast<int const volatile &&>(my_int); }233 234 int * f(index<10>) { return static_cast<int *>(&my_int); }235 int const * f(index<11>) { return static_cast<int const *>(&my_int); }236 int volatile * f(index<12>) { return static_cast<int volatile *>(&my_int); }237 int const volatile * f(index<13>) { return static_cast<int const volatile *>(&my_int); }238 239 template <int Func, class Expect>240 void test()241 {242 using RawInvokeResult = decltype(f(index<Func>{}));243 static_assert(std::is_same<RawInvokeResult, Expect>::value, "");244 using FnType = RawInvokeResult (*) (index<Func>);245 FnType fn = f;246 std::tuple<index<Func>> t; ((void)t);247 using InvokeResult = decltype(std::apply(fn, t));248 static_assert(std::is_same<InvokeResult, Expect>::value, "");249 }250} // namespace ReturnTypeTest251 252void test_return_type()253{254 using ReturnTypeTest::test;255 test<0, void>();256 test<1, int>();257 test<2, int &>();258 test<3, int const &>();259 test<4, int volatile &>();260 test<5, int const volatile &>();261 test<6, int &&>();262 test<7, int const &&>();263 test<8, int volatile &&>();264 test<9, int const volatile &&>();265 test<10, int *>();266 test<11, int const *>();267 test<12, int volatile *>();268 test<13, int const volatile *>();269}270 271int main(int, char**) {272 test_constexpr_evaluation();273 test_call_quals_and_arg_types();274 test_return_type();275 test_noexcept();276 277 return 0;278}279