From 29b65c98e720056e87334ce88a683969e57efd3d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 24 May 2022 08:46:22 +0200 Subject: Short live qxp::function_ref! This is an implementation of function_ref, which has been proposed for inclusion into C++23, but has not been accepted, yet, which is why we place it in namespace qxp (for eXPerimental) instead of q23. The implementation is based on wg21.link/P0792r9, which, at the time of writing, is the latest revision of the paper. It will be used in both QTestLib and qmldom. Fixes: QTBUG-103739 Change-Id: I52723eca28f7ac02ce7ce51928361d81ae5c92b1 Reviewed-by: Fabian Kosmale Reviewed-by: Qt CI Bot --- tests/auto/corelib/global/CMakeLists.txt | 1 + tests/auto/corelib/global/qxp/CMakeLists.txt | 1 + .../corelib/global/qxp/function_ref/CMakeLists.txt | 7 + .../qxp/function_ref/tst_qxp_function_ref.cpp | 234 +++++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 tests/auto/corelib/global/qxp/CMakeLists.txt create mode 100644 tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt create mode 100644 tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp (limited to 'tests/auto/corelib/global') diff --git a/tests/auto/corelib/global/CMakeLists.txt b/tests/auto/corelib/global/CMakeLists.txt index fe63402288..e19eab18eb 100644 --- a/tests/auto/corelib/global/CMakeLists.txt +++ b/tests/auto/corelib/global/CMakeLists.txt @@ -21,3 +21,4 @@ add_subdirectory(qoperatingsystemversion) if(WIN32) add_subdirectory(qwinregistry) endif() +add_subdirectory(qxp) diff --git a/tests/auto/corelib/global/qxp/CMakeLists.txt b/tests/auto/corelib/global/qxp/CMakeLists.txt new file mode 100644 index 0000000000..f289a04701 --- /dev/null +++ b/tests/auto/corelib/global/qxp/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(function_ref) diff --git a/tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt b/tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt new file mode 100644 index 0000000000..91c797c32f --- /dev/null +++ b/tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt @@ -0,0 +1,7 @@ +qt_internal_add_test(tst_qxp_function_ref + EXCEPTIONS + SOURCES + tst_qxp_function_ref.cpp + PUBLIC_LIBRARIES + Qt::Core +) diff --git a/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp b/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp new file mode 100644 index 0000000000..b17ead436a --- /dev/null +++ b/tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp @@ -0,0 +1,234 @@ +// 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 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::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 + { + qxp::function_ref f = [](int i) -> int { return i; }; + static_assert(std::is_same_v>); + } +#endif +} + + +QTEST_APPLESS_MAIN(tst_qxp_function_ref); + +#include "tst_qxp_function_ref.moc" -- cgit v1.2.3