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