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 | ~function_ref_base() = default; |
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 | std::negation<std::is_member_pointer<std::remove_reference_t<F>>>, |
112 | is_invocable_using<copy_const_t<Const, std::remove_reference_t<F>>&> |
113 | >, bool> = true |
114 | > |
115 | Q_IMPLICIT constexpr function_ref_base(F&& f) noexcept |
116 | : m_bound_entity(std::addressof(f)), |
117 | m_thunk_ptr([](BoundEntityType ctx, ArgTypes&&... args) noexcept(noex) -> R { |
118 | using That = copy_const_t<Const, std::remove_reference_t<F>>; |
119 | return q23::invoke_r<R>(*static_cast<That*>(ctx.obj), |
120 | std::forward<ArgTypes>(args)...); |
121 | }) |
122 | {} |
123 | |
124 | protected: |
125 | template < |
126 | class T, |
127 | std::enable_if_t<std::conjunction_v< |
128 | std::negation<std::is_same<q20::remove_cvref_t<T>, function_ref_base>>, |
129 | std::negation<std::is_pointer<T>> |
130 | >, bool> = true |
131 | > |
132 | function_ref_base& operator=(T) = delete; |
133 | |
134 | // Invocation [func.wrap.ref.inv] |
135 | R operator()(ArgTypes... args) const noexcept(noex) |
136 | { |
137 | return m_thunk_ptr(m_bound_entity, std::forward<ArgTypes>(args)...); |
138 | } |
139 | |
140 | }; |
141 | |
142 | } // namespace detail |
143 | |
144 | #define QT_SPECIALIZE_FUNCTION_REF(cv, noex) \ |
145 | template<class R, class... ArgTypes> \ |
146 | class function_ref<R(ArgTypes...) cv noexcept( noex )> \ |
147 | : private detail::function_ref_base< noex , cv void, R, ArgTypes...> \ |
148 | { \ |
149 | using base = detail::function_ref_base< noex , cv void, R, ArgTypes...>; \ |
150 | \ |
151 | public: \ |
152 | using base::base; \ |
153 | using base::operator(); \ |
154 | } \ |
155 | /* end */ |
156 | |
157 | QT_SPECIALIZE_FUNCTION_REF( , false); |
158 | QT_SPECIALIZE_FUNCTION_REF(const, false); |
159 | QT_SPECIALIZE_FUNCTION_REF( , true ); |
160 | QT_SPECIALIZE_FUNCTION_REF(const, true ); |
161 | |
162 | #undef QT_SPECIALIZE_FUNCTION_REF |
163 | |
164 | // deduction guides [func.wrap.ref.deduct] |
165 | |
166 | template < |
167 | class F, |
168 | std::enable_if_t<std::is_function_v<F>, bool> = true |
169 | > |
170 | function_ref(F*) -> function_ref<F>; |
171 | |
172 | } // namespace qxp |
173 | |
174 | QT_END_NAMESPACE |
175 | |
176 | #endif /* QXPFUNCTIONAL_H */ |
177 | |