summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/global/qxpfunctional.h176
-rw-r--r--tests/auto/corelib/global/CMakeLists.txt1
-rw-r--r--tests/auto/corelib/global/qxp/CMakeLists.txt1
-rw-r--r--tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt7
-rw-r--r--tests/auto/corelib/global/qxp/function_ref/tst_qxp_function_ref.cpp234
6 files changed, 420 insertions, 0 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index a802e1140d..47b9bd0d11 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -73,6 +73,7 @@ qt_internal_add_module(Core
global/q20algorithm.h
global/q20functional.h
global/q23functional.h
+ global/qxpfunctional.h
global/q20iterator.h
io/qabstractfileengine.cpp io/qabstractfileengine_p.h
io/qbuffer.cpp io/qbuffer.h
diff --git a/src/corelib/global/qxpfunctional.h b/src/corelib/global/qxpfunctional.h
new file mode 100644
index 0000000000..67350c56ed
--- /dev/null
+++ b/src/corelib/global/qxpfunctional.h
@@ -0,0 +1,176 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QXPFUNCTIONAL_H
+#define QXPFUNCTIONAL_H
+
+#include <QtCore/qglobal.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. Types and functions defined
+// in this file will behave exactly as their std counterparts. You
+// may use these definitions in your own code, but be aware that we
+// will remove them once Qt depends on the C++ version that supports
+// them in namespace std. There will be NO deprecation warning, the
+// definitions will JUST go away.
+//
+// If you can't agree to these terms, don't use these definitions!
+//
+// We mean it.
+//
+
+#include <QtCore/q23functional.h>
+#include <type_traits>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace qxp {
+// like P0792r9's function_ref:
+
+// [func.wrap.ref], non-owning wrapper
+template<class... S> class function_ref; // not defined
+
+// template<class R, class... ArgTypes>
+// class function_ref<R(ArgTypes...) cv noexcept(noex)>; // see below
+//
+// [func.wrap.ref.general]
+// The header provides partial specializations of function_ref for each combination
+// of the possible replacements of the placeholders cv and noex where:
+// - cv is either const or empty.
+// - noex is either true or false.
+
+namespace detail {
+
+template <typename T>
+using if_function = std::enable_if_t<std::is_function_v<T>, bool>;
+template <typename T>
+using if_non_function = std::enable_if_t<!std::is_function_v<T>, bool>;
+
+template <typename From, typename To>
+using copy_const_t = std::conditional_t<
+ std::is_const_v<From>,
+ std::add_const_t<To>,
+ To
+ >;
+
+template <class Const>
+union BoundEntityType {
+ template <typename F, if_function<F> = true>
+ explicit constexpr BoundEntityType(F *f)
+ : fun(reinterpret_cast<QFunctionPointer>(f)) {}
+ template <typename T, if_non_function<T> = true>
+ explicit constexpr BoundEntityType(T *t)
+ : obj(static_cast<Const*>(t)) {}
+ Const *obj;
+ QFunctionPointer fun;
+};
+
+template <bool noex, class Const, class R, class... ArgTypes>
+class function_ref_base
+{
+protected:
+ ~function_ref_base() = default;
+
+ using BoundEntityType = detail::BoundEntityType<Const>;
+
+ template <typename... Ts>
+ using is_invocable_using = std::conditional_t<
+ noex,
+ std::is_nothrow_invocable_r<R, Ts..., ArgTypes...>,
+ std::is_invocable_r<R, Ts..., ArgTypes...>
+ >;
+
+ using ThunkPtr = R(*)(BoundEntityType, ArgTypes&&...) noexcept(noex);
+
+ BoundEntityType m_bound_entity;
+ ThunkPtr m_thunk_ptr;
+
+public:
+ template<
+ class F,
+ std::enable_if_t<std::conjunction_v<
+ std::is_function<F>,
+ is_invocable_using<F>
+ >, bool> = true
+ >
+ Q_IMPLICIT function_ref_base(F* f) noexcept
+ : m_bound_entity(f),
+ m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R {
+ return q23::invoke_r<R>(reinterpret_cast<F*>(ctx.fun),
+ std::forward<ArgTypes>(args)...);
+ })
+ {}
+
+ template<
+ class F,
+ std::enable_if_t<std::conjunction_v<
+ std::negation<std::is_same<q20::remove_cvref_t<F>, function_ref_base>>,
+ std::negation<std::is_member_pointer<std::remove_reference_t<F>>>,
+ is_invocable_using<copy_const_t<Const, std::remove_reference_t<F>>&>
+ >, bool> = true
+ >
+ Q_IMPLICIT constexpr function_ref_base(F&& f) noexcept
+ : m_bound_entity(std::addressof(f)),
+ m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R {
+ using That = copy_const_t<Const, std::remove_reference_t<F>>;
+ return q23::invoke_r<R>(*static_cast<That*>(ctx.obj),
+ std::forward<ArgTypes>(args)...);
+ })
+ {}
+
+protected:
+ template <
+ class T,
+ std::enable_if_t<std::conjunction_v<
+ std::negation<std::is_same<q20::remove_cvref_t<T>, function_ref_base>>,
+ std::negation<std::is_pointer<T>>
+ >, bool> = true
+ >
+ function_ref_base& operator=(T) = delete;
+
+ // Invocation [func.wrap.ref.inv]
+ R operator()(ArgTypes... args) const noexcept(noex)
+ {
+ return m_thunk_ptr(m_bound_entity, std::forward<ArgTypes>(args)...);
+ }
+
+};
+
+} // namespace detail
+
+#define QT_SPECIALIZE_FUNCTION_REF(cv, noex) \
+ template<class R, class... ArgTypes> \
+ class function_ref<R(ArgTypes...) cv noexcept( noex )> \
+ : private detail::function_ref_base< noex , cv void, R, ArgTypes...> \
+ { \
+ using base = detail::function_ref_base< noex , cv void, R, ArgTypes...>; \
+ \
+ public: \
+ using base::base; \
+ using base::operator(); \
+ } \
+ /* end */
+
+QT_SPECIALIZE_FUNCTION_REF( , false);
+QT_SPECIALIZE_FUNCTION_REF(const, false);
+QT_SPECIALIZE_FUNCTION_REF( , true );
+QT_SPECIALIZE_FUNCTION_REF(const, true );
+
+#undef QT_SPECIALIZE_FUNCTION_REF
+
+// deduction guides [func.wrap.ref.deduct]
+
+template <
+ class F,
+ std::enable_if_t<std::is_function_v<F>, bool> = true
+>
+function_ref(F*) -> function_ref<F>;
+
+} // namespace qxp
+
+QT_END_NAMESPACE
+
+#endif /* QXPFUNCTIONAL_H */
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 <QtCore/qxpfunctional.h>
+
+#include <QTest>
+
+#include <type_traits>
+
+// checking dependency q20::remove_cvref_t:
+#define CHECK(in, out) \
+ static_assert(std::is_same_v<q20::remove_cvref_t< in >, 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 <typename T> constexpr inline bool
+is_noexcept_function_ref_helper_v = false;
+template <typename R, typename...Args> constexpr inline bool
+is_noexcept_function_ref_helper_v<qxp::function_ref<R(Args...) noexcept(true)>> = true;
+template <typename R, typename...Args> constexpr inline bool
+is_noexcept_function_ref_helper_v<qxp::function_ref<R(Args...) const noexcept(true)>> = true;
+
+template <typename T> constexpr inline bool
+is_noexcept_function_ref_v = is_noexcept_function_ref_helper_v<q20::remove_cvref_t<T>>;
+
+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<qxp::function_ref<int(int)>>);
+ static_assert(std::is_trivially_copyable_v<qxp::function_ref<int()>>);
+ static_assert(std::is_trivially_copyable_v<qxp::function_ref<void()>>);
+
+ {
+ Q_CONSTINIT static int invoked = 0;
+ auto lambda = [](int i) noexcept { ++invoked; return i; };
+ const qxp::function_ref<int(int)> f = lambda;
+ QCOMPARE(invoked, 0);
+ QCOMPARE(f(42), 42);
+ QCOMPARE(invoked, 1);
+
+ const int fourtyTwo = 42;
+
+ const qxp::function_ref<int(int) noexcept> 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<decltype(f3)>);
+ 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<decltype(f4)>);
+ 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<int()> f = lambda;
+ QCOMPARE(invoked, 0);
+ QCOMPARE(f(), 42);
+ QCOMPARE(invoked, 1);
+
+ const qxp::function_ref<int()> 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<decltype(f3)>);
+ QCOMPARE(invoked, 2);
+ QCOMPARE(f3(), 42);
+ QCOMPARE(invoked, 3);
+ }
+ {
+ Q_CONSTINIT static int invoked = 0;
+ auto lambda = [] { ++invoked; };
+ const qxp::function_ref<void()> f = lambda;
+ QCOMPARE(invoked, 0);
+ f();
+ QCOMPARE(invoked, 1);
+
+ const qxp::function_ref<void()> 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<int() const> callable)
+ {
+ return callable();
+ };
+ auto func_m = [](qxp::function_ref<int() /*mutable*/> 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<int(int)> 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<int(int) const> f = lambda;
+
+ QCOMPARE(invoked, 1);
+ QCOMPARE(f(51), 51);
+ QCOMPARE(invoked, 2);
+
+#if 0 // ### should this work?:
+ Q_CONSTINIT static qxp::function_ref<int(int)> 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<decltype(f), \
+ qxp::function_ref<sig>>); \
+ qxp::function_ref f2 = &fun; \
+ static_assert(std::is_same_v<decltype(f2), \
+ qxp::function_ref<sig>>); \
+ } 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<decltype(f),
+ qxp::function_ref<int(int)>>);
+ }
+#endif
+}
+
+
+QTEST_APPLESS_MAIN(tst_qxp_function_ref);
+
+#include "tst_qxp_function_ref.moc"