| 1 | // Copyright (C) 2022 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | #ifndef QXPFUNCTIONAL_H |
| 4 | #define QXPFUNCTIONAL_H |
| 5 | |
| 6 | #include <QtCore/qglobal.h> |
| 7 | |
| 8 | // |
| 9 | // W A R N I N G |
| 10 | // ------------- |
| 11 | // |
| 12 | // This file is not part of the Qt API. Types and functions defined in this |
| 13 | // file can reliably be replaced by their std counterparts, once available. |
| 14 | // You may use these definitions in your own code, but be aware that we |
| 15 | // will remove them once Qt depends on the C++ version that supports |
| 16 | // them in namespace std. There will be NO deprecation warning, the |
| 17 | // definitions will JUST go away. |
| 18 | // |
| 19 | // If you can't agree to these terms, don't use these definitions! |
| 20 | // |
| 21 | // We mean it. |
| 22 | // |
| 23 | |
| 24 | #include <QtCore/q23functional.h> |
| 25 | #include <QtCore/q20type_traits.h> |
| 26 | #include <utility> |
| 27 | |
| 28 | QT_BEGIN_NAMESPACE |
| 29 | |
| 30 | namespace qxp { |
| 31 | // like P0792r9's function_ref: |
| 32 | |
| 33 | // [func.wrap.ref], non-owning wrapper |
| 34 | template<class... S> class function_ref; // not defined |
| 35 | |
| 36 | // template<class R, class... ArgTypes> |
| 37 | // class function_ref<R(ArgTypes...) cv noexcept(noex)>; // see below |
| 38 | // |
| 39 | // [func.wrap.ref.general] |
| 40 | // The header provides partial specializations of function_ref for each combination |
| 41 | // of the possible replacements of the placeholders cv and noex where: |
| 42 | // - cv is either const or empty. |
| 43 | // - noex is either true or false. |
| 44 | |
| 45 | namespace detail { |
| 46 | |
| 47 | template <typename T> |
| 48 | using if_function = std::enable_if_t<std::is_function_v<T>, bool>; |
| 49 | template <typename T> |
| 50 | using if_non_function = std::enable_if_t<!std::is_function_v<T>, bool>; |
| 51 | |
| 52 | template <typename From, typename To> |
| 53 | using copy_const_t = std::conditional_t< |
| 54 | std::is_const_v<From>, |
| 55 | std::add_const_t<To>, |
| 56 | To |
| 57 | >; |
| 58 | |
| 59 | template <class Const> |
| 60 | union BoundEntityType { |
| 61 | template <typename F, if_function<F> = true> |
| 62 | explicit constexpr BoundEntityType(F *f) |
| 63 | : fun(reinterpret_cast<QFunctionPointer>(f)) {} |
| 64 | template <typename T, if_non_function<T> = true> |
| 65 | explicit constexpr BoundEntityType(T *t) |
| 66 | : obj(static_cast<Const*>(t)) {} |
| 67 | Const *obj; |
| 68 | QFunctionPointer fun; |
| 69 | }; |
| 70 | |
| 71 | template <bool noex, class Const, class R, class... ArgTypes> |
| 72 | class function_ref_base |
| 73 | { |
| 74 | protected: |
| 75 | QT_DECLARE_RO5_SMF_AS_DEFAULTED(function_ref_base) |
| 76 | |
| 77 | using BoundEntityType = detail::BoundEntityType<Const>; |
| 78 | |
| 79 | template <typename... Ts> |
| 80 | using is_invocable_using = std::conditional_t< |
| 81 | noex, |
| 82 | std::is_nothrow_invocable_r<R, Ts..., ArgTypes...>, |
| 83 | std::is_invocable_r<R, Ts..., ArgTypes...> |
| 84 | >; |
| 85 | |
| 86 | using ThunkPtr = R(*)(BoundEntityType, ArgTypes&&...) noexcept(noex); |
| 87 | |
| 88 | BoundEntityType m_bound_entity; |
| 89 | ThunkPtr m_thunk_ptr; |
| 90 | |
| 91 | public: |
| 92 | template< |
| 93 | class F, |
| 94 | std::enable_if_t<std::conjunction_v< |
| 95 | std::is_function<F>, |
| 96 | is_invocable_using<F> |
| 97 | >, bool> = true |
| 98 | > |
| 99 | Q_IMPLICIT function_ref_base(F* f) noexcept |
| 100 | : m_bound_entity(f), |
| 101 | m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R { |
| 102 | return q23::invoke_r<R>(reinterpret_cast<F*>(ctx.fun), |
| 103 | std::forward<ArgTypes>(args)...); |
| 104 | }) |
| 105 | {} |
| 106 | |
| 107 | template< |
| 108 | class F, |
| 109 | std::enable_if_t<std::conjunction_v< |
| 110 | std::negation<std::is_same<q20::remove_cvref_t<F>, function_ref_base>>, |
| 111 | #ifdef Q_OS_VXWORKS |
| 112 | // The VxWorks compiler is trying to match this ctor against |
| 113 | // qxp::function_ref in lieu of using the copy-constructor, so ban |
| 114 | // matching against the equivalent qxp::function_ref here. |
| 115 | // This doesn't change anything on other platforms, so to save |
| 116 | // on compile-speed, enable it only for VxWorks: |
| 117 | std::negation< |
| 118 | std::is_same< |
| 119 | q20::remove_cvref_t<F>, |
| 120 | std::conditional_t< |
| 121 | std::is_const_v<Const>, |
| 122 | qxp::function_ref<R(ArgTypes...) const noexcept(noex)>, |
| 123 | qxp::function_ref<R(ArgTypes...) noexcept(noex)> |
| 124 | > |
| 125 | > |
| 126 | >, |
| 127 | #endif // Q_OS_VXWORKS |
| 128 | std::negation<std::is_member_pointer<std::remove_reference_t<F>>>, |
| 129 | is_invocable_using<copy_const_t<Const, std::remove_reference_t<F>>&> |
| 130 | >, bool> = true |
| 131 | > |
| 132 | Q_IMPLICIT constexpr function_ref_base(F&& f) noexcept |
| 133 | : m_bound_entity(std::addressof(f)), |
| 134 | m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R { |
| 135 | using That = copy_const_t<Const, std::remove_reference_t<F>>; |
| 136 | return q23::invoke_r<R>(*static_cast<That*>(ctx.obj), |
| 137 | std::forward<ArgTypes>(args)...); |
| 138 | }) |
| 139 | {} |
| 140 | |
| 141 | protected: |
| 142 | template < |
| 143 | class T, |
| 144 | std::enable_if_t<std::negation_v< |
| 145 | std::disjunction< |
| 146 | std::is_same<T, function_ref_base>, |
| 147 | std::is_pointer<T> |
| 148 | > |
| 149 | >, bool> = true |
| 150 | > |
| 151 | function_ref_base& operator=(T) = delete; |
| 152 | |
| 153 | // Invocation [func.wrap.ref.inv] |
| 154 | R operator()(ArgTypes... args) const noexcept(noex) |
| 155 | { |
| 156 | return m_thunk_ptr(m_bound_entity, std::forward<ArgTypes>(args)...); |
| 157 | } |
| 158 | |
| 159 | }; |
| 160 | |
| 161 | } // namespace detail |
| 162 | |
| 163 | #define QT_SPECIALIZE_FUNCTION_REF(cv, noex) \ |
| 164 | template<class R, class... ArgTypes> \ |
| 165 | class function_ref<R(ArgTypes...) cv noexcept( noex )> \ |
| 166 | : private detail::function_ref_base< noex , cv void, R, ArgTypes...> \ |
| 167 | { \ |
| 168 | using base = detail::function_ref_base< noex , cv void, R, ArgTypes...>; \ |
| 169 | \ |
| 170 | public: \ |
| 171 | using base::base; \ |
| 172 | using base::operator(); \ |
| 173 | } \ |
| 174 | /* end */ |
| 175 | |
| 176 | QT_SPECIALIZE_FUNCTION_REF( , false); |
| 177 | QT_SPECIALIZE_FUNCTION_REF(const, false); |
| 178 | QT_SPECIALIZE_FUNCTION_REF( , true ); |
| 179 | QT_SPECIALIZE_FUNCTION_REF(const, true ); |
| 180 | |
| 181 | #undef QT_SPECIALIZE_FUNCTION_REF |
| 182 | |
| 183 | // deduction guides [func.wrap.ref.deduct] |
| 184 | |
| 185 | template < |
| 186 | class F, |
| 187 | detail::if_function<F> = true |
| 188 | > |
| 189 | function_ref(F*) -> function_ref<F>; |
| 190 | |
| 191 | } // namespace qxp |
| 192 | |
| 193 | QT_END_NAMESPACE |
| 194 | |
| 195 | #endif /* QXPFUNCTIONAL_H */ |
| 196 | |