"ScL Feature"/
SCL Feature 0.0.1
C++20 composable proxy wrapper with pluggable executor strategy
Loading...
Searching...
No Matches
has_qualified_method.h
Go to the documentation of this file.
1#pragma once
2
5
6#include <scl/utility/preprocessor/forward.h>
7
8#include <type_traits>
9
15#define SCL_HQM_CALL(method, quals) \
16 ::std::declval<Obj quals>().method(::std::declval<S_c_L_Args_>()...)
17
23#define SCL_HQM_SAME(method, left_quals, right_quals) \
24 ::std::is_same_v<decltype(SCL_HQM_CALL(method, left_quals)), decltype(SCL_HQM_CALL(method, right_quals))>
25
121
122#define SCL_HAS_QUALIFIED_METHOD(method, Type, ...) \
123 ([]<typename S_c_L_Type_, typename... S_c_L_Args_>() -> bool { \
124 using Obj = ::std::remove_cvref_t<S_c_L_Type_>; \
125 using Base = ::std::remove_reference_t<S_c_L_Type_>; \
126 \
127 constexpr bool is_obj_rvalue = ::std::is_rvalue_reference_v<S_c_L_Type_>; \
128 constexpr bool is_obj_lvalue = ::std::is_lvalue_reference_v<S_c_L_Type_>; \
129 constexpr bool is_obj_const = ::std::is_const_v<Base>; \
130 constexpr bool is_obj_volatile = ::std::is_volatile_v<Base>; \
131 constexpr bool is_obj_cv = is_obj_const && is_obj_volatile; \
132 \
133 /* --- lvalue callability --- */ \
134 constexpr bool can_lv_m = requires { SCL_HQM_CALL(method, &); }; \
135 constexpr bool can_lv_c = requires { SCL_HQM_CALL(method, const &); }; \
136 constexpr bool can_lv_v = requires { SCL_HQM_CALL(method, volatile &); }; \
137 constexpr bool can_lv_cv = requires { SCL_HQM_CALL(method, const volatile &); }; \
138 \
139 /* --- rvalue callability --- */ \
140 constexpr bool can_rv_m = requires { SCL_HQM_CALL(method, &&); }; \
141 constexpr bool can_rv_c = requires { SCL_HQM_CALL(method, const &&); }; \
142 constexpr bool can_rv_v = requires { SCL_HQM_CALL(method, volatile &&); }; \
143 constexpr bool can_rv_cv = requires { SCL_HQM_CALL(method, const volatile &&); }; \
144 \
145 /* --- lvalue qualifier detection --- */ \
146 /* Return-type comparisons (SCL_HQM_SAME) are guarded by if-constexpr */ \
147 /* callability checks so that decltype is never evaluated on an ill-formed call. */ \
148 if constexpr (is_obj_lvalue) \
149 { \
150 if constexpr (is_obj_cv) \
151 { \
152 return can_lv_cv; \
153 } \
154 else if constexpr (is_obj_volatile || is_obj_const) \
155 { \
156 constexpr bool can_narrow = is_obj_volatile ? can_lv_v : can_lv_c; \
157 constexpr bool can_wider = can_lv_cv; \
158 if constexpr (!can_narrow) \
159 return false; \
160 else if constexpr (!can_wider) \
161 return true; \
162 else \
163 return !(is_obj_volatile \
164 ? SCL_HQM_SAME(method, volatile &, const volatile &) \
165 : SCL_HQM_SAME(method, const &, const volatile &)); \
166 } \
167 else \
168 { \
169 /* mutable lvalue: must differ from every wider qualifier that is callable. */ \
170 if constexpr (!can_lv_m) \
171 return false; \
172 else \
173 { \
174 if constexpr (can_lv_c) \
175 { \
176 if constexpr (SCL_HQM_SAME(method, &, const &)) \
177 return false; \
178 } \
179 if constexpr (can_lv_v) \
180 { \
181 if constexpr (SCL_HQM_SAME(method, &, volatile &)) \
182 return false; \
183 } \
184 if constexpr (can_lv_cv) \
185 { \
186 if constexpr (SCL_HQM_SAME(method, &, const volatile &)) \
187 return false; \
188 } \
189 return true; \
190 } \
191 } \
192 } \
193 /* --- rvalue qualifier detection --- */ \
194 else if constexpr (is_obj_rvalue) \
195 { \
196 if constexpr (is_obj_cv) \
197 { \
198 return can_rv_cv; \
199 } \
200 else if constexpr (is_obj_volatile) \
201 { \
202 if constexpr (!can_rv_v) \
203 return false; \
204 else if constexpr (!can_rv_cv) \
205 return true; \
206 else \
207 return !SCL_HQM_SAME(method, volatile &&, const volatile &&); \
208 } \
209 else if constexpr (is_obj_const) \
210 { \
211 /* MSVC cross-category check: const& is callable on rvalues (non-standard). */ \
212 /* Detect implicit binding by comparing return types across categories. */ \
213 if constexpr (!can_rv_c) \
214 return false; \
215 else \
216 { \
217 if constexpr (can_rv_cv) \
218 { \
219 if constexpr (SCL_HQM_SAME(method, const &&, const volatile &&)) \
220 return false; \
221 } \
222 if constexpr (can_lv_c) \
223 { \
224 if constexpr (SCL_HQM_SAME(method, const &&, const &)) \
225 { \
226 if constexpr (!can_rv_m) \
227 return false; \
228 else \
229 { \
230 constexpr bool scl_same_rv_ = SCL_HQM_SAME(method, &&, const &&); \
231 if constexpr (scl_same_rv_) \
232 return false; \
233 } \
234 } \
235 } \
236 return true; \
237 } \
238 } \
239 else \
240 { \
241 /* mutable rvalue: must differ from every wider qualifier that is callable. */ \
242 if constexpr (!can_rv_m) \
243 return false; \
244 else \
245 { \
246 if constexpr (can_rv_c) \
247 { \
248 if constexpr (SCL_HQM_SAME(method, &&, const &&)) \
249 return false; \
250 } \
251 if constexpr (can_rv_v) \
252 { \
253 if constexpr (SCL_HQM_SAME(method, &&, volatile &&)) \
254 return false; \
255 } \
256 if constexpr (can_rv_cv) \
257 { \
258 if constexpr (SCL_HQM_SAME(method, &&, const volatile &&)) \
259 return false; \
260 } \
261 return true; \
262 } \
263 } \
264 } \
265 else \
266 { \
267 return false; \
268 } \
269 }).template operator()<SCL_FORWARD(Type), ##__VA_ARGS__>()