// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include // checking dependency q20::remove_cvref_t: #define CHECK(in, out) \ static_assert(std::is_same_v, out >) CHECK(int, int); CHECK(const int, int); CHECK(int &, int); CHECK(const int &, int); CHECK(int &&, int); CHECK(const int &&, int); CHECK(int *, int *); CHECK(const int *, const int *); CHECK(int[4], int[4]); CHECK(const int (&)[4], int[4]); #undef CHECK template constexpr inline bool is_noexcept_function_ref_helper_v = false; template constexpr inline bool is_noexcept_function_ref_helper_v> = true; template constexpr inline bool is_noexcept_function_ref_helper_v> = true; template constexpr inline bool is_noexcept_function_ref_v = is_noexcept_function_ref_helper_v>; class tst_qxp_function_ref : public QObject { Q_OBJECT public: using QObject::QObject; private Q_SLOTS: void basics(); void constOverloads(); void constExpr(); void voidReturning(); void ctad(); }; void tst_qxp_function_ref::basics() { static_assert(std::is_trivially_copyable_v>); static_assert(std::is_trivially_copyable_v>); static_assert(std::is_trivially_copyable_v>); { Q_CONSTINIT static int invoked = 0; auto lambda = [](int i) noexcept { ++invoked; return i; }; const qxp::function_ref f = lambda; QCOMPARE(invoked, 0); QCOMPARE(f(42), 42); QCOMPARE(invoked, 1); const int fourtyTwo = 42; const qxp::function_ref f2 = std::move(lambda); QCOMPARE(invoked, 1); QCOMPARE(f2(fourtyTwo), 42); QCOMPARE(invoked, 2); int (*fpr)(int) = lambda; const qxp::function_ref f3 = fpr; static_assert(!is_noexcept_function_ref_v); QCOMPARE(invoked, 2); QCOMPARE(f3(42), 42); QCOMPARE(invoked, 3); int (*fpr2)(int) noexcept = lambda; const qxp::function_ref f4 = fpr2; static_assert(is_noexcept_function_ref_v); QCOMPARE(invoked, 3); QCOMPARE(f4(42), 42); QCOMPARE(invoked, 4); } { Q_CONSTINIT static int invoked = 0; auto lambda = [] { ++invoked; return 42; }; const qxp::function_ref f = lambda; QCOMPARE(invoked, 0); QCOMPARE(f(), 42); QCOMPARE(invoked, 1); const qxp::function_ref f2 = std::move(lambda); QCOMPARE(invoked, 1); QCOMPARE(f2(), 42); QCOMPARE(invoked, 2); int (*fpr)() = lambda; const qxp::function_ref f3 = fpr; static_assert(!is_noexcept_function_ref_v); QCOMPARE(invoked, 2); QCOMPARE(f3(), 42); QCOMPARE(invoked, 3); } { Q_CONSTINIT static int invoked = 0; auto lambda = [] { ++invoked; }; const qxp::function_ref f = lambda; QCOMPARE(invoked, 0); f(); QCOMPARE(invoked, 1); const qxp::function_ref f2 = std::move(lambda); QCOMPARE(invoked, 1); f2(); QCOMPARE(invoked, 2); void (*fpr)() = lambda; const qxp::function_ref f3 = fpr; QCOMPARE(invoked, 2); f3(); QCOMPARE(invoked, 3); } } void tst_qxp_function_ref::constOverloads() { auto func_c = [](qxp::function_ref callable) { return callable(); }; auto func_m = [](qxp::function_ref callable) { return callable(); }; struct S { int operator()() { return 1; } int operator()() const { return 2; } }; S s; QCOMPARE(func_c(s), 2); QCOMPARE(func_m(s), 1); const S cs; QCOMPARE(func_c(cs), 2); #if 0 // this should not compile (and doesn't, but currently fails with an error in the impl, // not by failing a constructor constaint → spec issue?). QCOMPARE(func_m(cs), 2); #endif } void tst_qxp_function_ref::constExpr() { Q_CONSTINIT static int invoked = 0; { Q_CONSTINIT static auto lambda = [] (int i) { ++invoked; return i; }; // the function object constructor is constexpr, so this should be constinit: Q_CONSTINIT static qxp::function_ref f = lambda; QCOMPARE(invoked, 0); QCOMPARE(f(15), 15); QCOMPARE(invoked, 1); } { constexpr static auto lambda = [] (int i) { ++invoked; return i; }; // the function object constructor is constexpr, so this should be constinit: Q_CONSTINIT static qxp::function_ref f = lambda; QCOMPARE(invoked, 1); QCOMPARE(f(51), 51); QCOMPARE(invoked, 2); #if 0 // ### should this work?: Q_CONSTINIT static qxp::function_ref f2 = lambda; QCOMPARE(invoked, 2); QCOMPARE(f(150), 150); QCOMPARE(invoked, 3); #endif } } int i_f_i_nx(int i) noexcept { return i; } void v_f_i_nx(int) noexcept {} int i_f_v_nx() noexcept { return 42; } void v_f_v_nx() noexcept {} int i_f_i_ex(int i) { return i; } void v_f_i_ex(int) {} int i_f_v_ex() { return 42; } void v_f_v_ex() {} void tst_qxp_function_ref::voidReturning() { // check that "casting" int to void returns works: using Fi = qxp::function_ref; using Fv = qxp::function_ref; { Fi fi = i_f_i_nx; fi(42); Fv fv = i_f_v_nx; fv(); } { Fi fi = i_f_i_ex; fi(42); Fv fv = i_f_v_ex; fv(); } // now with lambdas bool ok = false; // prevent lambdas from decaying to function pointers { auto lambda1 = [&](int i) noexcept { return i + int(ok); }; Fi fi = lambda1; fi(42); auto lambda2 = [&]() noexcept { return int(ok); }; Fv fv = lambda2; fv(); } { auto lambda1 = [&](int i) { return i + int(ok); }; Fi fi = lambda1; fi(42); auto lambda2 = [&]() { return int(ok); }; Fv fv = lambda2; fv(); } } void tst_qxp_function_ref::ctad() { #define CHECK(fun, sig) \ do { \ qxp::function_ref f = fun; \ static_assert(std::is_same_v>); \ qxp::function_ref f2 = &fun; \ static_assert(std::is_same_v>); \ } while (false) CHECK(i_f_i_nx, int (int) noexcept); CHECK(v_f_i_nx, void(int) noexcept); CHECK(i_f_v_nx, int ( ) noexcept); CHECK(v_f_v_nx, void( ) noexcept); CHECK(i_f_i_ex, int (int)); CHECK(v_f_i_ex, void(int)); CHECK(i_f_v_ex, int ( )); CHECK(v_f_v_ex, void( )); #undef CHECK #if 0 // no deduction guides for the non-function-pointer case, so no CTAD for lambdas { auto lambda = [](int i) -> int { return i; }; qxp::function_ref f = lambda; static_assert(std::is_same_v>); } #endif } QTEST_APPLESS_MAIN(tst_qxp_function_ref); #include "tst_qxp_function_ref.moc"