Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | pmbaty | 1 | /* |
2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
3 | * It is copyright by its individual contributors, as recorded in the |
||
4 | * project's Git history. See COPYING.txt at the top level for license |
||
5 | * terms and a link to the Git history. |
||
6 | */ |
||
7 | #pragma once |
||
8 | |||
9 | #include <stdexcept> |
||
10 | #include <string> |
||
11 | #include <type_traits> |
||
12 | #include "fwd-valptridx.h" |
||
13 | #include "pack.h" |
||
14 | #include "compiler-poison.h" |
||
15 | #include "selfiter.h" |
||
16 | #include <array> |
||
17 | |||
18 | #ifdef DXX_CONSTANT_TRUE |
||
19 | #define DXX_VALPTRIDX_STATIC_CHECK(SUCCESS_CONDITION,FAILURE_FUNCTION,FAILURE_STRING) \ |
||
20 | static_cast<void>(DXX_CONSTANT_TRUE(!SUCCESS_CONDITION) && \ |
||
21 | (DXX_ALWAYS_ERROR_FUNCTION(FAILURE_FUNCTION, FAILURE_STRING), 0) \ |
||
22 | ) \ |
||
23 | |||
24 | #ifdef DXX_HAVE_ATTRIBUTE_WARNING |
||
25 | /* This causes many warnings because some conversions are not checked for |
||
26 | * safety. Eliminating the warnings by changing the call sites to check first |
||
27 | * would be a useful improvement. |
||
28 | */ |
||
29 | //#define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT __attribute__((__warning__("call not eliminated"))) |
||
30 | #endif |
||
31 | #else |
||
32 | #define DXX_VALPTRIDX_STATIC_CHECK(E,F,S) |
||
33 | #endif |
||
34 | |||
35 | #define DXX_VALPTRIDX_CHECK(SUCCESS_CONDITION,ERROR,FAILURE_STRING,...) \ |
||
36 | ( DXX_BEGIN_COMPOUND_STATEMENT { \ |
||
37 | const bool dxx_valptridx_check_success_condition = (SUCCESS_CONDITION); \ |
||
38 | DXX_VALPTRIDX_STATIC_CHECK(dxx_valptridx_check_success_condition, dxx_trap_##ERROR, FAILURE_STRING); \ |
||
39 | static_cast<void>( \ |
||
40 | dxx_valptridx_check_success_condition || (ERROR::report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VA(__VA_ARGS__)), 0) \ |
||
41 | ); \ |
||
42 | } DXX_END_COMPOUND_STATEMENT ) |
||
43 | |||
44 | #ifndef DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
45 | #define DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
46 | #endif |
||
47 | |||
48 | template <typename managed_type> |
||
49 | class valptridx<managed_type>::array_base_count_type |
||
50 | { |
||
51 | protected: |
||
52 | union { |
||
53 | unsigned count; |
||
54 | /* |
||
55 | * Use DXX_VALPTRIDX_FOR_EACH_PPI_TYPE to generate empty union |
||
56 | * members based on basic_{i,v}val_member_factory |
||
57 | * specializations. |
||
58 | */ |
||
59 | #define DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES(MANAGED_TYPE, DERIVED_TYPE_PREFIX, CONTEXT, PISUFFIX, IVPREFIX, MCPREFIX) \ |
||
60 | valptridx<MANAGED_TYPE>::f ## IVPREFIX ## MCPREFIX ## PISUFFIX \ |
||
61 | IVPREFIX ## MCPREFIX ## PISUFFIX |
||
62 | DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES, managed_type,,); |
||
63 | #undef DXX_VALPTRIDX_DEFINE_MEMBER_FACTORIES |
||
64 | }; |
||
65 | constexpr array_base_count_type() : |
||
66 | count(0) |
||
67 | { |
||
68 | } |
||
69 | public: |
||
70 | unsigned get_count() const |
||
71 | { |
||
72 | return count; |
||
73 | } |
||
74 | void set_count(const unsigned c) |
||
75 | { |
||
76 | count = c; |
||
77 | } |
||
78 | }; |
||
79 | |||
80 | template < |
||
81 | typename INTEGRAL_TYPE, |
||
82 | std::size_t array_size_value, |
||
83 | valptridx_detail::untyped_utilities::report_error_style report_const_error_value, |
||
84 | valptridx_detail::untyped_utilities::report_error_style report_mutable_error_value |
||
85 | > |
||
86 | constexpr std::integral_constant<std::size_t, array_size_value> valptridx_detail::specialized_type_parameters<INTEGRAL_TYPE, array_size_value, report_const_error_value, report_mutable_error_value>::array_size; |
||
87 | |||
88 | class valptridx_detail::untyped_utilities::report_error_undefined |
||
89 | { |
||
90 | public: |
||
91 | __attribute_cold |
||
92 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
93 | static void report(...) |
||
94 | { |
||
95 | } |
||
96 | }; |
||
97 | |||
98 | class valptridx_detail::untyped_utilities::report_error_trap_terse |
||
99 | { |
||
100 | public: |
||
101 | __attribute_cold |
||
102 | __attribute_noreturn |
||
103 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
104 | static void report(...) |
||
105 | { |
||
106 | __builtin_trap(); |
||
107 | } |
||
108 | }; |
||
109 | |||
110 | class valptridx_detail::untyped_utilities::index_mismatch_trap_verbose |
||
111 | { |
||
112 | public: |
||
113 | __attribute_cold |
||
114 | __attribute_noreturn |
||
115 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
116 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index, const void *const expected_pointer, const void *const actual_pointer) |
||
117 | { |
||
118 | __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index), "rm" (expected_pointer), "rm" (actual_pointer)); |
||
119 | __builtin_trap(); |
||
120 | } |
||
121 | }; |
||
122 | |||
123 | class valptridx_detail::untyped_utilities::index_range_trap_verbose |
||
124 | { |
||
125 | public: |
||
126 | __attribute_cold |
||
127 | __attribute_noreturn |
||
128 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
129 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array, const unsigned long supplied_index) |
||
130 | { |
||
131 | __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array), "rm" (supplied_index)); |
||
132 | __builtin_trap(); |
||
133 | } |
||
134 | }; |
||
135 | |||
136 | class valptridx_detail::untyped_utilities::null_pointer_trap_verbose |
||
137 | { |
||
138 | public: |
||
139 | __attribute_cold |
||
140 | __attribute_noreturn |
||
141 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
142 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS) |
||
143 | { |
||
144 | __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_N_VARS); |
||
145 | __builtin_trap(); |
||
146 | } |
||
147 | __attribute_cold |
||
148 | __attribute_noreturn |
||
149 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
150 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const void *const array) |
||
151 | { |
||
152 | __asm__ __volatile__("" :: DXX_VALPTRIDX_REPORT_STANDARD_ASM_LOAD_COMMA_R_VARS "rm" (array)); |
||
153 | __builtin_trap(); |
||
154 | } |
||
155 | }; |
||
156 | |||
157 | template <typename P> |
||
158 | class valptridx<P>::index_mismatch_exception : |
||
159 | public std::logic_error |
||
160 | { |
||
161 | DXX_INHERIT_CONSTRUCTORS(index_mismatch_exception, logic_error); |
||
162 | public: |
||
163 | __attribute_cold |
||
164 | __attribute_noreturn |
||
165 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
166 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, index_type, const_pointer_type, const_pointer_type); |
||
167 | }; |
||
168 | |||
169 | template <typename P> |
||
170 | class valptridx<P>::index_range_exception : |
||
171 | public std::out_of_range |
||
172 | { |
||
173 | DXX_INHERIT_CONSTRUCTORS(index_range_exception, out_of_range); |
||
174 | public: |
||
175 | __attribute_cold |
||
176 | __attribute_noreturn |
||
177 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
178 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *, long); |
||
179 | }; |
||
180 | |||
181 | template <typename P> |
||
182 | class valptridx<P>::null_pointer_exception : |
||
183 | public std::logic_error |
||
184 | { |
||
185 | DXX_INHERIT_CONSTRUCTORS(null_pointer_exception, logic_error); |
||
186 | public: |
||
187 | __attribute_cold |
||
188 | __attribute_noreturn |
||
189 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
190 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DEFN_VARS); |
||
191 | __attribute_cold |
||
192 | __attribute_noreturn |
||
193 | DXX_VALPTRIDX_WARN_CALL_NOT_OPTIMIZED_OUT |
||
194 | static void report(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const array_managed_type *); |
||
195 | }; |
||
196 | |||
197 | template <typename managed_type> |
||
198 | template <typename handle_index_mismatch> |
||
199 | void valptridx<managed_type>::check_index_match(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type r, index_type i, const array_managed_type &a __attribute_unused) |
||
200 | { |
||
201 | const auto pi = &a[i]; |
||
202 | DXX_VALPTRIDX_CHECK(pi == &r, handle_index_mismatch, "pointer/index mismatch", &a, i, pi, &r); |
||
203 | } |
||
204 | |||
205 | template <typename managed_type> |
||
206 | template <typename handle_index_range_error, template <typename> class Compare> |
||
207 | typename valptridx<managed_type>::index_type valptridx<managed_type>::check_index_range(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const index_type i, const array_managed_type *const a) |
||
208 | { |
||
209 | const std::size_t ss = i; |
||
210 | DXX_VALPTRIDX_CHECK(Compare<std::size_t>()(ss, array_size), handle_index_range_error, "invalid index used in array subscript", a, ss); |
||
211 | return i; |
||
212 | } |
||
213 | |||
214 | template <typename managed_type> |
||
215 | template <typename handle_null_pointer> |
||
216 | void valptridx<managed_type>::check_null_pointer_conversion(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p) |
||
217 | { |
||
218 | DXX_VALPTRIDX_CHECK(p, handle_null_pointer, "NULL pointer converted"); |
||
219 | } |
||
220 | |||
221 | template <typename managed_type> |
||
222 | template <typename handle_null_pointer> |
||
223 | void valptridx<managed_type>::check_null_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_pointer_type p, const array_managed_type &a __attribute_unused) |
||
224 | { |
||
225 | DXX_VALPTRIDX_CHECK(p, handle_null_pointer, "NULL pointer used", &a); |
||
226 | } |
||
227 | |||
228 | template <typename managed_type> |
||
229 | template <typename handle_index_mismatch, typename handle_index_range_error> |
||
230 | void valptridx<managed_type>::check_implicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const managed_type &r, const array_managed_type &a) |
||
231 | { |
||
232 | check_explicit_index_range_ref<handle_index_mismatch, handle_index_range_error>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, static_cast<const_pointer_type>(&r) - static_cast<const_pointer_type>(&a.front()), a); |
||
233 | } |
||
234 | |||
235 | template <typename managed_type> |
||
236 | template <typename handle_index_mismatch, typename handle_index_range_error> |
||
237 | void valptridx<managed_type>::check_explicit_index_range_ref(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const_reference_type &r, std::size_t i, const array_managed_type &a) |
||
238 | { |
||
239 | check_index_match<handle_index_mismatch>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, i, a); |
||
240 | check_index_range<handle_index_range_error>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a); |
||
241 | } |
||
242 | |||
243 | template <typename managed_type> |
||
244 | class valptridx<managed_type>::partial_policy::require_valid |
||
245 | { |
||
246 | public: |
||
247 | static constexpr std::false_type allow_nullptr{}; |
||
248 | static constexpr std::false_type check_allowed_invalid_index(index_type) { return {}; } |
||
249 | static constexpr bool check_nothrow_index(index_type i) |
||
250 | { |
||
251 | return std::less<std::size_t>()(i, array_size); |
||
252 | } |
||
253 | }; |
||
254 | |||
255 | template <typename managed_type> |
||
256 | class valptridx<managed_type>::partial_policy::allow_invalid |
||
257 | { |
||
258 | public: |
||
259 | static constexpr std::true_type allow_nullptr{}; |
||
260 | static constexpr bool check_allowed_invalid_index(index_type i) |
||
261 | { |
||
262 | return i == static_cast<index_type>(~0); |
||
263 | } |
||
264 | static constexpr bool check_nothrow_index(index_type i) |
||
265 | { |
||
266 | return check_allowed_invalid_index(i) || require_valid::check_nothrow_index(i); |
||
267 | } |
||
268 | }; |
||
269 | |||
270 | template <typename managed_type> |
||
271 | constexpr std::false_type valptridx<managed_type>::partial_policy::require_valid::allow_nullptr; |
||
272 | |||
273 | template <typename managed_type> |
||
274 | constexpr std::true_type valptridx<managed_type>::partial_policy::allow_invalid::allow_nullptr; |
||
275 | |||
276 | template <typename managed_type> |
||
277 | template <template <typename> class policy> |
||
278 | class valptridx<managed_type>::partial_policy::apply_cv_policy |
||
279 | { |
||
280 | template <typename T> |
||
281 | using apply_cv_qualifier = typename policy<T>::type; |
||
282 | public: |
||
283 | using array_managed_type = apply_cv_qualifier<valptridx<managed_type>::array_managed_type>; |
||
284 | using pointer_type = apply_cv_qualifier<managed_type> *; |
||
285 | using reference_type = apply_cv_qualifier<managed_type> &; |
||
286 | }; |
||
287 | |||
288 | template <typename managed_type> |
||
289 | class valptridx<managed_type>::vc : |
||
290 | public partial_policy::require_valid, |
||
291 | public partial_policy::template apply_cv_policy<std::add_const> |
||
292 | { |
||
293 | }; |
||
294 | |||
295 | template <typename managed_type> |
||
296 | class valptridx<managed_type>::vm : |
||
297 | public partial_policy::require_valid, |
||
298 | public partial_policy::template apply_cv_policy<std::remove_const> |
||
299 | { |
||
300 | }; |
||
301 | |||
302 | template <typename managed_type> |
||
303 | class valptridx<managed_type>::ic : |
||
304 | public partial_policy::allow_invalid, |
||
305 | public partial_policy::template apply_cv_policy<std::add_const> |
||
306 | { |
||
307 | }; |
||
308 | |||
309 | template <typename managed_type> |
||
310 | class valptridx<managed_type>::im : |
||
311 | public partial_policy::allow_invalid, |
||
312 | public partial_policy::template apply_cv_policy<std::remove_const> |
||
313 | { |
||
314 | }; |
||
315 | |||
316 | template <typename managed_type> |
||
317 | template <typename policy> |
||
318 | class valptridx<managed_type>::idx : |
||
319 | public policy |
||
320 | { |
||
321 | using containing_type = valptridx<managed_type>; |
||
322 | public: |
||
323 | using policy::allow_nullptr; |
||
324 | using policy::check_allowed_invalid_index; |
||
325 | using index_type = typename containing_type::index_type; |
||
326 | using integral_type = typename containing_type::integral_type; |
||
327 | using typename policy::array_managed_type; |
||
328 | idx() = delete; |
||
329 | idx(const idx &) = default; |
||
330 | idx(idx &&) = default; |
||
331 | idx &operator=(const idx &) & = default; |
||
332 | idx &operator=(idx &&) & = default; |
||
333 | idx &operator=(const idx &) && = delete; |
||
334 | idx &operator=(idx &&) && = delete; |
||
335 | |||
336 | index_type get_unchecked_index() const { return m_idx; } |
||
337 | |||
338 | /* If moving from allow_invalid to allow_invalid, no check is |
||
339 | * needed. |
||
340 | * If moving from require_valid to anything, no check is needed. |
||
341 | */ |
||
342 | template <typename rpolicy, typename std::enable_if<policy::allow_nullptr || !rpolicy::allow_nullptr, int>::type = 0> |
||
343 | idx(const idx<rpolicy> &rhs) : |
||
344 | m_idx(rhs.get_unchecked_index()) |
||
345 | { |
||
346 | } |
||
347 | template <typename rpolicy, typename std::enable_if<!(policy::allow_nullptr || !rpolicy::allow_nullptr), int>::type = 0> |
||
348 | idx(const idx<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) : |
||
349 | m_idx(rhs.get_unchecked_index()) |
||
350 | { |
||
351 | /* If moving from allow_invalid to require_valid, check range. |
||
352 | */ |
||
353 | check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS m_idx, nullptr); |
||
354 | } |
||
355 | template <typename rpolicy> |
||
356 | idx(idx<rpolicy> &&rhs) : |
||
357 | m_idx(rhs.get_unchecked_index()) |
||
358 | { |
||
359 | /* Prevent move from allow_invalid into require_valid. The |
||
360 | * right hand side must be saved and checked for validity before |
||
361 | * being used to initialize a require_valid type. |
||
362 | */ |
||
363 | static_assert(policy::allow_nullptr || !rpolicy::allow_nullptr, "cannot move from allow_invalid to require_valid"); |
||
364 | } |
||
365 | idx(index_type i DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) : |
||
366 | m_idx(check_allowed_invalid_index(i) ? i : check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, nullptr)) |
||
367 | { |
||
368 | } |
||
369 | idx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) : |
||
370 | m_idx(check_allowed_invalid_index(i) ? i : check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a)) |
||
371 | { |
||
372 | } |
||
373 | protected: |
||
374 | template <integral_type v> |
||
375 | idx(const magic_constant<v> &, const allow_none_construction *) : |
||
376 | m_idx(v) |
||
377 | { |
||
378 | static_assert(!allow_nullptr, "allow_none_construction used where nullptr was already legal"); |
||
379 | static_assert(static_cast<std::size_t>(v) >= array_size, "allow_none_construction used with valid index"); |
||
380 | } |
||
381 | idx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *) : |
||
382 | m_idx(check_index_range<index_range_error_type<array_managed_type>, std::less_equal>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a)) |
||
383 | { |
||
384 | } |
||
385 | idx(index_type i, array_managed_type &, const assume_nothrow_index *) : |
||
386 | m_idx(i) |
||
387 | { |
||
388 | } |
||
389 | public: |
||
390 | template <integral_type v> |
||
391 | constexpr idx(const magic_constant<v> &) : |
||
392 | m_idx(v) |
||
393 | { |
||
394 | static_assert(allow_nullptr || static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy"); |
||
395 | } |
||
396 | template <typename rpolicy> |
||
397 | bool operator==(const idx<rpolicy> &rhs) const |
||
398 | { |
||
399 | return m_idx == rhs.get_unchecked_index(); |
||
400 | } |
||
401 | bool operator==(const index_type &i) const |
||
402 | { |
||
403 | return m_idx == i; |
||
404 | } |
||
405 | template <integral_type v> |
||
406 | bool operator==(const magic_constant<v> &) const |
||
407 | { |
||
408 | static_assert(allow_nullptr || static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy"); |
||
409 | return m_idx == v; |
||
410 | } |
||
411 | template <typename R> |
||
412 | bool operator!=(const R &rhs) const |
||
413 | { |
||
414 | return !(*this == rhs); |
||
415 | } |
||
416 | operator index_type() const |
||
417 | { |
||
418 | return m_idx; |
||
419 | } |
||
420 | protected: |
||
421 | index_type m_idx; |
||
422 | idx &operator++() |
||
423 | { |
||
424 | ++ m_idx; |
||
425 | return *this; |
||
426 | } |
||
427 | }; |
||
428 | |||
429 | template <typename managed_type> |
||
430 | template <typename policy> |
||
431 | class valptridx<managed_type>::ptr : |
||
432 | public policy |
||
433 | { |
||
434 | using containing_type = valptridx<managed_type>; |
||
435 | public: |
||
436 | using policy::allow_nullptr; |
||
437 | using policy::check_allowed_invalid_index; |
||
438 | using index_type = typename containing_type::index_type; |
||
439 | using const_pointer_type = typename containing_type::const_pointer_type; |
||
440 | using mutable_pointer_type = typename containing_type::mutable_pointer_type; |
||
441 | using allow_none_construction = typename containing_type::allow_none_construction; |
||
442 | using typename policy::array_managed_type; |
||
443 | using typename policy::pointer_type; |
||
444 | using typename policy::reference_type; |
||
445 | |||
446 | ptr() = delete; |
||
447 | /* Override template matches to make same-type copy/move trivial */ |
||
448 | ptr(const ptr &) = default; |
||
449 | ptr(ptr &&) = default; |
||
450 | ptr &operator=(const ptr &) & = default; |
||
451 | ptr &operator=(ptr &&) & = default; |
||
452 | ptr &operator=(const ptr &) && = delete; |
||
453 | ptr &operator=(ptr &&) && = delete; |
||
454 | |||
455 | pointer_type get_unchecked_pointer() const { return m_ptr; } |
||
456 | pointer_type get_nonnull_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const |
||
457 | { |
||
458 | /* If !allow_nullptr, assume nullptr was caught at construction. */ |
||
459 | if (allow_nullptr) |
||
460 | check_null_pointer_conversion<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS m_ptr); |
||
461 | return m_ptr; |
||
462 | } |
||
463 | reference_type get_checked_reference(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const |
||
464 | { |
||
465 | return *get_nonnull_pointer(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VA()); |
||
466 | } |
||
467 | |||
468 | ptr(std::nullptr_t) : |
||
469 | m_ptr(nullptr) |
||
470 | { |
||
471 | static_assert(allow_nullptr, "nullptr construction not allowed for this policy"); |
||
472 | } |
||
473 | template <integral_type v> |
||
474 | ptr(const magic_constant<v> &) : |
||
475 | m_ptr(nullptr) |
||
476 | { |
||
477 | static_assert(static_cast<std::size_t>(v) >= array_size, "valid magic index requires an array"); |
||
478 | static_assert(allow_nullptr || static_cast<std::size_t>(v) < array_size, "invalid magic index not allowed for this policy"); |
||
479 | } |
||
480 | template <integral_type v> |
||
481 | ptr(const magic_constant<v> &, array_managed_type &a) : |
||
482 | m_ptr(&a[v]) |
||
483 | { |
||
484 | static_assert(static_cast<std::size_t>(v) < array_size, "valid magic index required when using array"); |
||
485 | } |
||
486 | template <typename rpolicy, typename std::enable_if<policy::allow_nullptr || !rpolicy::allow_nullptr, int>::type = 0> |
||
487 | ptr(const ptr<rpolicy> &rhs) : |
||
488 | m_ptr(rhs.get_unchecked_pointer()) |
||
489 | { |
||
490 | } |
||
491 | template <typename rpolicy, typename std::enable_if<!(policy::allow_nullptr || !rpolicy::allow_nullptr), int>::type = 0> |
||
492 | ptr(const ptr<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) : |
||
493 | m_ptr(rhs.get_unchecked_pointer()) |
||
494 | { |
||
495 | check_null_pointer_conversion<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS m_ptr); |
||
496 | } |
||
497 | template <typename rpolicy> |
||
498 | ptr(ptr<rpolicy> &&rhs) : |
||
499 | m_ptr(rhs.get_unchecked_pointer()) |
||
500 | { |
||
501 | /* Prevent move from allow_invalid into require_valid. The |
||
502 | * right hand side must be saved and checked for validity before |
||
503 | * being used to initialize a require_valid type. |
||
504 | */ |
||
505 | static_assert(policy::allow_nullptr || !rpolicy::allow_nullptr, "cannot move from allow_invalid to require_valid"); |
||
506 | } |
||
507 | ptr(index_type i) = delete; |
||
508 | ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) : |
||
509 | m_ptr(check_allowed_invalid_index(i) ? nullptr : &a[check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a)]) |
||
510 | { |
||
511 | } |
||
512 | ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *) : |
||
513 | m_ptr(&a[check_index_range<index_range_error_type<array_managed_type>, std::less_equal>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, &a)]) |
||
514 | { |
||
515 | } |
||
516 | ptr(index_type i, array_managed_type &a, const assume_nothrow_index *) : |
||
517 | m_ptr(&a[i]) |
||
518 | { |
||
519 | } |
||
520 | ptr(pointer_type p) = delete; |
||
521 | ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, array_managed_type &a) : |
||
522 | /* No array consistency check here, since some code incorrectly |
||
523 | * defines instances of `object` outside the Objects array, then |
||
524 | * passes pointers to those instances to this function. |
||
525 | */ |
||
526 | m_ptr(p) |
||
527 | { |
||
528 | if (!allow_nullptr) |
||
529 | check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a); |
||
530 | } |
||
531 | ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS reference_type r, array_managed_type &a) : |
||
532 | m_ptr((check_implicit_index_range_ref<index_mismatch_error_type<array_managed_type>, index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, a), &r)) |
||
533 | { |
||
534 | } |
||
535 | ptr(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS reference_type r, index_type i, array_managed_type &a) : |
||
536 | m_ptr((check_explicit_index_range_ref<index_mismatch_error_type<array_managed_type>, index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS r, i, a), &r)) |
||
537 | { |
||
538 | } |
||
539 | |||
540 | operator mutable_pointer_type() const { return m_ptr; } // implicit pointer conversion deprecated |
||
541 | operator const_pointer_type() const { return m_ptr; } // implicit pointer conversion deprecated |
||
542 | template <typename rpolicy> |
||
543 | typename std::enable_if<!std::is_same<policy, rpolicy>::value, ptr>::type rebind_policy(ptr<rpolicy> &&rhs) const |
||
544 | { |
||
545 | /* This method could be marked as `static`, but is non-static so |
||
546 | * that callers must possess an instance of the target type. |
||
547 | * This serves as a basic check against casts that could remove |
||
548 | * `const` incorrectly. |
||
549 | */ |
||
550 | return ptr(std::move(rhs), static_cast<const typename containing_type::rebind_policy *>(nullptr)); |
||
551 | } |
||
552 | pointer_type operator->() const & |
||
553 | { |
||
554 | return get_nonnull_pointer(); |
||
555 | } |
||
556 | operator reference_type() const & |
||
557 | { |
||
558 | return get_checked_reference(); |
||
559 | } |
||
560 | reference_type operator*() const & |
||
561 | { |
||
562 | return get_checked_reference(); |
||
563 | } |
||
564 | explicit operator bool() const & |
||
565 | { |
||
566 | return !(*this == nullptr); |
||
567 | } |
||
568 | pointer_type operator->() const && |
||
569 | { |
||
570 | static_assert(!allow_nullptr, "operator-> not allowed with allow_invalid policy"); |
||
571 | return operator->(); |
||
572 | } |
||
573 | operator reference_type() const && |
||
574 | { |
||
575 | static_assert(!allow_nullptr, "implicit reference not allowed with allow_invalid policy"); |
||
576 | return *this; |
||
577 | } |
||
578 | reference_type operator*() const && |
||
579 | { |
||
580 | static_assert(!allow_nullptr, "operator* not allowed with allow_invalid policy"); |
||
581 | return *this; |
||
582 | } |
||
583 | explicit operator bool() const && = delete; |
||
584 | bool operator==(std::nullptr_t) const |
||
585 | { |
||
586 | static_assert(allow_nullptr, "nullptr comparison not allowed: value is never null"); |
||
587 | return m_ptr == nullptr; |
||
588 | } |
||
589 | bool operator==(const_pointer_type p) const |
||
590 | { |
||
591 | return m_ptr == p; |
||
592 | } |
||
593 | bool operator==(mutable_pointer_type p) const |
||
594 | { |
||
595 | return m_ptr == p; |
||
596 | } |
||
597 | template <typename rpolicy> |
||
598 | bool operator==(const ptr<rpolicy> &rhs) const |
||
599 | { |
||
600 | return *this == rhs.get_unchecked_pointer(); |
||
601 | } |
||
602 | template <typename R> |
||
603 | bool operator!=(const R &rhs) const |
||
604 | { |
||
605 | return !(*this == rhs); |
||
606 | } |
||
607 | template <typename U> |
||
608 | long operator-(U) const = delete; |
||
609 | template <typename R> |
||
610 | bool operator<(R) const = delete; |
||
611 | template <typename R> |
||
612 | bool operator>(R) const = delete; |
||
613 | template <typename R> |
||
614 | bool operator<=(R) const = delete; |
||
615 | template <typename R> |
||
616 | bool operator>=(R) const = delete; |
||
617 | protected: |
||
618 | pointer_type m_ptr; |
||
619 | ptr &operator++() |
||
620 | { |
||
621 | ++ m_ptr; |
||
622 | return *this; |
||
623 | } |
||
624 | ptr(const allow_none_construction *) : |
||
625 | m_ptr(nullptr) |
||
626 | { |
||
627 | static_assert(!allow_nullptr, "allow_none_construction used where nullptr was already legal"); |
||
628 | } |
||
629 | template <typename rpolicy> |
||
630 | ptr(ptr<rpolicy> &&rhs, const typename containing_type::rebind_policy *) : |
||
631 | m_ptr(const_cast<managed_type *>(rhs.get_unchecked_pointer())) |
||
632 | { |
||
633 | static_assert(allow_nullptr || !rpolicy::allow_nullptr, "cannot rebind from allow_invalid to require_valid"); |
||
634 | } |
||
635 | }; |
||
636 | |||
637 | #if DXX_VALPTRIDX_ENFORCE_STRICT_PI_SEPARATION |
||
638 | template <typename T> |
||
639 | struct strong_typedef : T |
||
640 | { |
||
641 | using T::T; |
||
642 | template <typename O, typename std::enable_if<std::is_constructible<T, O &&>::value, int>::type = 0> |
||
643 | strong_typedef(O &&o) : |
||
644 | T(std::forward<O>(o)) |
||
645 | { |
||
646 | } |
||
647 | strong_typedef() = default; |
||
648 | strong_typedef(const strong_typedef &) = default; |
||
649 | strong_typedef(strong_typedef &&) = default; |
||
650 | strong_typedef &operator=(const strong_typedef &) & = default; |
||
651 | strong_typedef &operator=(strong_typedef &&) & = default; |
||
652 | strong_typedef &operator=(const strong_typedef &) && = delete; |
||
653 | strong_typedef &operator=(strong_typedef &&) && = delete; |
||
654 | }; |
||
655 | #endif |
||
656 | |||
657 | template <typename managed_type> |
||
658 | template <typename policy> |
||
659 | class valptridx<managed_type>::ptridx : |
||
660 | public prohibit_void_ptr<ptridx<policy>>, |
||
661 | public ptr<policy>, |
||
662 | public idx<policy> |
||
663 | { |
||
664 | using containing_type = valptridx<managed_type>; |
||
665 | public: |
||
666 | using vptr_type = ptr<policy>; |
||
667 | using vidx_type = idx<policy>; |
||
668 | using typename vidx_type::array_managed_type; |
||
669 | using index_type = typename vidx_type::index_type; |
||
670 | using typename vidx_type::integral_type; |
||
671 | using typename vptr_type::pointer_type; |
||
672 | using vidx_type::operator==; |
||
673 | using vptr_type::operator==; |
||
674 | ptridx(const ptridx &) = default; |
||
675 | ptridx(ptridx &&) = default; |
||
676 | ptridx &operator=(const ptridx &) & = default; |
||
677 | ptridx &operator=(ptridx &&) & = default; |
||
678 | ptridx &operator=(const ptridx &) && = delete; |
||
679 | ptridx &operator=(ptridx &&) && = delete; |
||
680 | ptridx(std::nullptr_t) = delete; |
||
681 | /* Prevent implicit conversion. Require use of the factory function. |
||
682 | */ |
||
683 | ptridx(pointer_type p) = delete; |
||
684 | template <typename rpolicy, typename std::enable_if<policy::allow_nullptr || !rpolicy::allow_nullptr, int>::type = 0> |
||
685 | ptridx(const ptridx<rpolicy> &rhs) : |
||
686 | vptr_type(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs)), |
||
687 | vidx_type(static_cast<const typename ptridx<rpolicy>::vidx_type &>(rhs)) |
||
688 | { |
||
689 | } |
||
690 | template <typename rpolicy, typename std::enable_if<!(policy::allow_nullptr || !rpolicy::allow_nullptr), int>::type = 0> |
||
691 | ptridx(const ptridx<rpolicy> &rhs DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) : |
||
692 | vptr_type(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs) DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_PASS_VARS), |
||
693 | vidx_type(static_cast<const typename ptridx<rpolicy>::vidx_type &>(rhs) DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_PASS_VARS) |
||
694 | { |
||
695 | } |
||
696 | template <typename rpolicy> |
||
697 | ptridx(ptridx<rpolicy> &&rhs) : |
||
698 | vptr_type(static_cast<typename ptridx<rpolicy>::vptr_type &&>(rhs)), |
||
699 | vidx_type(static_cast<typename ptridx<rpolicy>::vidx_type &&>(rhs)) |
||
700 | { |
||
701 | } |
||
702 | template <integral_type v> |
||
703 | ptridx(const magic_constant<v> &m) : |
||
704 | vptr_type(m), |
||
705 | vidx_type(m) |
||
706 | { |
||
707 | } |
||
708 | template <integral_type v> |
||
709 | ptridx(const magic_constant<v> &m, array_managed_type &a) : |
||
710 | vptr_type(m, a), |
||
711 | vidx_type(m) |
||
712 | { |
||
713 | } |
||
714 | template <integral_type v> |
||
715 | ptridx(const magic_constant<v> &m, const allow_none_construction *const n) : |
||
716 | vptr_type(n), |
||
717 | vidx_type(m, n) |
||
718 | { |
||
719 | } |
||
720 | ptridx(index_type i) = delete; |
||
721 | ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a) : |
||
722 | vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a), |
||
723 | vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a) |
||
724 | { |
||
725 | } |
||
726 | ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS index_type i, array_managed_type &a, const allow_end_construction *e) : |
||
727 | vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a, e), |
||
728 | vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a, e) |
||
729 | { |
||
730 | } |
||
731 | ptridx(index_type i, array_managed_type &a, const assume_nothrow_index *e) : |
||
732 | vptr_type(i, a, e), |
||
733 | vidx_type(i, a, e) |
||
734 | { |
||
735 | } |
||
736 | ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, array_managed_type &a) : |
||
737 | /* Null pointer is never allowed when an index must be computed. |
||
738 | * Check for null, then use the reference constructor for |
||
739 | * vptr_type to avoid checking again. |
||
740 | */ |
||
741 | vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS (check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a), *p), a), |
||
742 | vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p - static_cast<pointer_type>(&a.front()), a) |
||
743 | { |
||
744 | } |
||
745 | ptridx(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS pointer_type p, index_type i, array_managed_type &a) : |
||
746 | vptr_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS (check_null_pointer<null_pointer_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a), *p), i, a), |
||
747 | vidx_type(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a) |
||
748 | { |
||
749 | } |
||
750 | template <typename rpolicy> |
||
751 | typename std::enable_if<!std::is_same<policy, rpolicy>::value, ptridx>::type rebind_policy(ptridx<rpolicy> &&rhs) const |
||
752 | { |
||
753 | return ptridx(std::move(rhs), static_cast<const typename containing_type::rebind_policy *>(nullptr)); |
||
754 | } |
||
755 | ptridx absolute_sibling(const index_type i DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const |
||
756 | { |
||
757 | static_assert(!policy::allow_nullptr, "absolute_sibling not allowed with invalid ptridx"); |
||
758 | ptridx r(*this); |
||
759 | r.m_ptr += check_index_range<index_range_error_type<array_managed_type>>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, nullptr) - this->m_idx; |
||
760 | r.m_idx = i; |
||
761 | return r; |
||
762 | } |
||
763 | template <typename rpolicy> |
||
764 | bool operator==(const ptridx<rpolicy> &rhs) const |
||
765 | { |
||
766 | return vptr_type::operator==(static_cast<const typename ptridx<rpolicy>::vptr_type &>(rhs)); |
||
767 | } |
||
768 | template <typename R> |
||
769 | bool operator!=(const R &rhs) const |
||
770 | { |
||
771 | return !(*this == rhs); |
||
772 | } |
||
773 | protected: |
||
774 | ptridx &operator++() |
||
775 | { |
||
776 | vptr_type::operator++(); |
||
777 | vidx_type::operator++(); |
||
778 | return *this; |
||
779 | } |
||
780 | template <typename rpolicy> |
||
781 | ptridx(ptridx<rpolicy> &&rhs, const typename containing_type::rebind_policy *const rebind) : |
||
782 | vptr_type(static_cast<typename ptridx<rpolicy>::vptr_type &&>(rhs), rebind), |
||
783 | vidx_type(static_cast<typename ptridx<rpolicy>::vidx_type &&>(rhs)) |
||
784 | { |
||
785 | /* No static_assert for policy compatibility. Incompatible |
||
786 | * policy conversions will be trapped by the static_assert in |
||
787 | * `vptr_type`. |
||
788 | */ |
||
789 | } |
||
790 | }; |
||
791 | |||
792 | template <typename managed_type> |
||
793 | template <typename guarded_type> |
||
794 | class valptridx<managed_type>::guarded |
||
795 | { |
||
796 | static_assert(std::is_trivially_destructible<guarded_type>::value, "non-trivial destructor found for guarded_type"); |
||
797 | enum state : uint8_t |
||
798 | { |
||
799 | /* empty - the untrusted input was invalid, so no guarded_type |
||
800 | * exists |
||
801 | */ |
||
802 | empty, |
||
803 | /* initialized - the untrusted input was valid, so a |
||
804 | * guarded_type type exists, but the calling code has not yet |
||
805 | * tested the state of this guarded<P> |
||
806 | */ |
||
807 | initialized, |
||
808 | /* checked - the untrusted input was valid, and the calling code |
||
809 | * has called operator bool() |
||
810 | */ |
||
811 | checked, |
||
812 | }; |
||
813 | union { |
||
814 | state m_dummy; |
||
815 | guarded_type m_value; |
||
816 | }; |
||
817 | mutable state m_state; |
||
818 | public: |
||
819 | __attribute_cold |
||
820 | guarded(std::nullptr_t) : |
||
821 | m_dummy(), m_state(empty) |
||
822 | { |
||
823 | } |
||
824 | guarded(guarded_type &&v) : |
||
825 | m_value(std::move(v)), m_state(initialized) |
||
826 | { |
||
827 | } |
||
828 | __attribute_warn_unused_result |
||
829 | explicit operator bool() const |
||
830 | { |
||
831 | /* |
||
832 | * If no contained guarded_type exists, return false. |
||
833 | * Otherwise, record that the result has been tested and then |
||
834 | * return true. operator*() uses m_state to enforce that the |
||
835 | * result is tested. |
||
836 | */ |
||
837 | if (unlikely(m_state == empty)) |
||
838 | return false; |
||
839 | m_state = checked; |
||
840 | return true; |
||
841 | } |
||
842 | __attribute_warn_unused_result |
||
843 | guarded_type operator*() const & |
||
844 | { |
||
845 | /* |
||
846 | * Correct code will always execute as if this method was just |
||
847 | * the return statement, with none of the sanity checks. The |
||
848 | * checks are present to catch misuse of this type, preferably |
||
849 | * at compile-time, but at least at runtime. |
||
850 | */ |
||
851 | #define DXX_VALPTRIDX_GUARDED_OBJECT_NO "access to guarded object that does not exist" |
||
852 | #define DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE "access to guarded object that may not exist" |
||
853 | #ifdef DXX_CONSTANT_TRUE |
||
854 | { |
||
855 | /* |
||
856 | * If the validity has not been verified by the caller, then |
||
857 | * fail. Choose an error message and function name |
||
858 | * based on whether the contained type provably does not |
||
859 | * exist. It provably does not exist if this call is on a |
||
860 | * path where operator bool() returned false. It |
||
861 | * conditionally might not exist if this call is on a path |
||
862 | * where operator bool() has not been called. |
||
863 | */ |
||
864 | if (DXX_CONSTANT_TRUE(m_state == empty)) |
||
865 | DXX_ALWAYS_ERROR_FUNCTION(guarded_accessed_empty, DXX_VALPTRIDX_GUARDED_OBJECT_NO); |
||
866 | /* If the contained object might not exist: */ |
||
867 | if (DXX_CONSTANT_TRUE(m_state == initialized)) |
||
868 | DXX_ALWAYS_ERROR_FUNCTION(guarded_accessed_unchecked, DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE); |
||
869 | } |
||
870 | #endif |
||
871 | /* |
||
872 | * If the compiler does not offer constant truth analysis |
||
873 | * (perhaps because of insufficient optimization), then emit a |
||
874 | * runtime check for whether the guarded_type exists. |
||
875 | * |
||
876 | * This test can throw even if the contained object is valid, if |
||
877 | * the caller did not first validate that the contained object |
||
878 | * is valid. This restriction is necessary since inputs are |
||
879 | * usually valid even when untested, so throwing only on state |
||
880 | * `empty` would allow incorrect usage to persist in the code |
||
881 | * until someone happened to receive an invalid input from an |
||
882 | * untrusted source. |
||
883 | */ |
||
884 | if (m_state != checked) |
||
885 | throw std::logic_error(m_state == empty ? DXX_VALPTRIDX_GUARDED_OBJECT_NO : DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE); |
||
886 | #undef DXX_VALPTRIDX_GUARDED_OBJECT_MAYBE |
||
887 | #undef DXX_VALPTRIDX_GUARDED_OBJECT_NO |
||
888 | return m_value; |
||
889 | } |
||
890 | guarded_type operator*() const && = delete; |
||
891 | }; |
||
892 | |||
893 | template <typename managed_type> |
||
894 | class valptridx<managed_type>::array_managed_type : |
||
895 | public array_base_count_type, |
||
896 | public array_base_storage_type |
||
897 | { |
||
898 | public: |
||
899 | /* |
||
900 | * Expose the union members that act as factory methods. Leave the |
||
901 | * `count` union member protected. |
||
902 | */ |
||
903 | #define DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES(MANAGED_TYPE, DERIVED_TYPE_PREFIX, CONTEXT, PISUFFIX, IVPREFIX, MCPREFIX) \ |
||
904 | using array_base_count_type::IVPREFIX ## MCPREFIX ## PISUFFIX |
||
905 | DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES,,,); |
||
906 | #undef DXX_VALPTRIDX_ACCESS_SUBTYPE_MEMBER_FACTORIES |
||
907 | using typename array_base_storage_type::reference; |
||
908 | using typename array_base_storage_type::const_reference; |
||
909 | reference operator[](const integral_type &n) |
||
910 | { |
||
911 | return array_base_storage_type::operator[](n); |
||
912 | } |
||
913 | const_reference operator[](const integral_type &n) const |
||
914 | { |
||
915 | return array_base_storage_type::operator[](n); |
||
916 | } |
||
917 | template <typename T> |
||
918 | reference operator[](const T &) const = delete; |
||
919 | #if DXX_HAVE_POISON_UNDEFINED |
||
920 | array_managed_type(); |
||
921 | #else |
||
922 | array_managed_type() = default; |
||
923 | #endif |
||
924 | array_managed_type(const array_managed_type &) = delete; |
||
925 | array_managed_type &operator=(const array_managed_type &) = delete; |
||
926 | }; |
||
927 | |||
928 | template <typename managed_type> |
||
929 | template <typename Pc, typename Pm> |
||
930 | class valptridx<managed_type>::basic_ival_member_factory |
||
931 | { |
||
932 | using containing_type = valptridx<managed_type>; |
||
933 | protected: |
||
934 | /* |
||
935 | * These casts are well-defined: |
||
936 | * - The reinterpret_cast is defined because |
||
937 | * `basic_ival_member_factory` is a base of a member of the |
||
938 | * anonymous union in `array_base_count_type`. |
||
939 | * - The anonymous union in `array_base_count_type` is the only |
||
940 | * member of that type, so its storage must be aligned to the |
||
941 | * beginning of the object. |
||
942 | * - `basic_ival_member_factory` and its derivatives are not used |
||
943 | * anywhere other than `array_base_count_type`, so any call to |
||
944 | * these methods must be on an instance used in |
||
945 | * `array_base_count_type`. |
||
946 | * |
||
947 | * - The static_cast is defined because `array_base_count_type` is a |
||
948 | * non-virtual base of `array_managed_type`. |
||
949 | * - `array_base_count_type` is not used as a base for any class |
||
950 | * other than `array_managed_type`, nor used freestanding, so any |
||
951 | * instance of `array_base_count_type` must be safe to downcast to |
||
952 | * `array_managed_type`. |
||
953 | */ |
||
954 | constexpr const array_managed_type &get_array() const |
||
955 | { |
||
956 | return static_cast<const array_managed_type &>(reinterpret_cast<const array_base_count_type &>(*this)); |
||
957 | } |
||
958 | array_managed_type &get_array() |
||
959 | { |
||
960 | return static_cast<array_managed_type &>(reinterpret_cast<array_base_count_type &>(*this)); |
||
961 | } |
||
962 | template <typename P, typename A> |
||
963 | static guarded<P> check_untrusted_internal(const index_type i, A &a) |
||
964 | { |
||
965 | if (P::check_nothrow_index(i)) |
||
966 | return P(i, a, static_cast<const assume_nothrow_index *>(nullptr)); |
||
967 | else |
||
968 | return nullptr; |
||
969 | } |
||
970 | template <typename P, typename T, typename A> |
||
971 | static guarded<P> check_untrusted_internal(T &&, A &) = delete; |
||
972 | template <typename P, typename A> |
||
973 | __attribute_warn_unused_result |
||
974 | /* C++ does not allow `static operator()()`, so name it |
||
975 | * `call_operator` instead. |
||
976 | */ |
||
977 | static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::index_type i, A &a) |
||
978 | { |
||
979 | return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a); |
||
980 | } |
||
981 | template <typename P, containing_type::integral_type v, typename A> |
||
982 | __attribute_warn_unused_result |
||
983 | static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const containing_type::magic_constant<v> &m, A &a) |
||
984 | { |
||
985 | /* |
||
986 | * All call_operator definitions must have the macro which |
||
987 | * defines filename/lineno, but the magic_constant overload |
||
988 | * has no need for them. |
||
989 | * |
||
990 | * Cast them to void to silence the warning about unused |
||
991 | * parameters. |
||
992 | */ |
||
993 | DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_VOID_VARS(); |
||
994 | return P(m, a); |
||
995 | } |
||
996 | template <typename P, typename T, typename A> |
||
997 | static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS T &&, A &a) = delete; |
||
998 | basic_ival_member_factory() = default; |
||
999 | public: |
||
1000 | basic_ival_member_factory(const basic_ival_member_factory &) = delete; |
||
1001 | basic_ival_member_factory &operator=(const basic_ival_member_factory &) = delete; |
||
1002 | void *operator &() const = delete; |
||
1003 | template <typename T> |
||
1004 | __attribute_warn_unused_result |
||
1005 | guarded<Pc> check_untrusted(T &&t) const |
||
1006 | { |
||
1007 | return this->template check_untrusted_internal<Pc>(static_cast<T &&>(t), get_array()); |
||
1008 | } |
||
1009 | template <typename T> |
||
1010 | __attribute_warn_unused_result |
||
1011 | guarded<Pm> check_untrusted(T &&t) |
||
1012 | { |
||
1013 | return this->template check_untrusted_internal<Pm>(static_cast<T &&>(t), get_array()); |
||
1014 | } |
||
1015 | template <typename T> |
||
1016 | __attribute_warn_unused_result |
||
1017 | Pc operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const |
||
1018 | { |
||
1019 | return this->template call_operator<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array()); |
||
1020 | } |
||
1021 | template <typename T> |
||
1022 | __attribute_warn_unused_result |
||
1023 | Pm operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) |
||
1024 | { |
||
1025 | return this->template call_operator<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array()); |
||
1026 | } |
||
1027 | }; |
||
1028 | |||
1029 | template <typename managed_type> |
||
1030 | template <typename Pc, typename Pm> |
||
1031 | class valptridx<managed_type>::basic_vval_member_factory : |
||
1032 | public basic_ival_member_factory<Pc, Pm> |
||
1033 | { |
||
1034 | protected: |
||
1035 | using basic_ival_member_factory<Pc, Pm>::get_array; |
||
1036 | using basic_ival_member_factory<Pc, Pm>::call_operator; |
||
1037 | template <typename P> |
||
1038 | using iterator = self_return_iterator<P>; |
||
1039 | template <typename P, typename policy, typename A> |
||
1040 | static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename valptridx<managed_type>::template wrapper<valptridx<managed_type>::idx<policy>> i, A &a) |
||
1041 | { |
||
1042 | return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS i, a); |
||
1043 | } |
||
1044 | template <typename P> |
||
1045 | __attribute_warn_unused_result |
||
1046 | static P call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::mutable_pointer_type p, typename P::array_managed_type &a) |
||
1047 | { |
||
1048 | return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a); |
||
1049 | } |
||
1050 | /* |
||
1051 | * When P is a const type, enable an overload that takes a const |
||
1052 | * array. Otherwise, enable a deleted overload that takes a mutable |
||
1053 | * array. This provides a slightly clearer error when trying to |
||
1054 | * pass a const pointer to a mutable factory. |
||
1055 | */ |
||
1056 | template <typename P> |
||
1057 | __attribute_warn_unused_result |
||
1058 | static typename std::enable_if<std::is_same<const array_managed_type, typename P::array_managed_type>::value, P>::type call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::const_pointer_type p, const array_managed_type &a) |
||
1059 | { |
||
1060 | return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS p, a); |
||
1061 | } |
||
1062 | template <typename P> |
||
1063 | static typename std::enable_if<!std::is_same<const array_managed_type, typename P::array_managed_type>::value, P>::type call_operator(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS const typename P::const_pointer_type p, array_managed_type &a) = delete; |
||
1064 | template <typename P, typename A> |
||
1065 | static iterator<P> end_internal(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_DEFN_VARS A &a) |
||
1066 | { |
||
1067 | return P(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<index_type>(a.get_count()), a, static_cast<const allow_end_construction *>(nullptr)); |
||
1068 | } |
||
1069 | public: |
||
1070 | __attribute_warn_unused_result |
||
1071 | typename array_base_storage_type::size_type count() const |
||
1072 | { |
||
1073 | return get_array().get_count(); |
||
1074 | } |
||
1075 | __attribute_warn_unused_result |
||
1076 | typename array_base_storage_type::size_type size() const |
||
1077 | { |
||
1078 | return get_array().size(); |
||
1079 | } |
||
1080 | template <typename T> |
||
1081 | __attribute_warn_unused_result |
||
1082 | Pc operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) const |
||
1083 | { |
||
1084 | return this->template call_operator<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array()); |
||
1085 | } |
||
1086 | template <typename T> |
||
1087 | __attribute_warn_unused_result |
||
1088 | Pm operator()(T &&t DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_L_DECL_VARS) |
||
1089 | { |
||
1090 | return this->template call_operator<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS static_cast<T &&>(t), get_array()); |
||
1091 | } |
||
1092 | __attribute_warn_unused_result |
||
1093 | iterator<Pc> begin() const |
||
1094 | { |
||
1095 | return Pc(valptridx<managed_type>::magic_constant<0>(), get_array()); |
||
1096 | } |
||
1097 | __attribute_warn_unused_result |
||
1098 | iterator<Pm> begin() |
||
1099 | { |
||
1100 | return Pm(valptridx<managed_type>::magic_constant<0>(), get_array()); |
||
1101 | } |
||
1102 | __attribute_warn_unused_result |
||
1103 | iterator<Pc> end(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) const |
||
1104 | { |
||
1105 | return this->template end_internal<Pc>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS get_array()); |
||
1106 | } |
||
1107 | __attribute_warn_unused_result |
||
1108 | iterator<Pm> end(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_N_DECL_VARS) |
||
1109 | { |
||
1110 | return this->template end_internal<Pm>(DXX_VALPTRIDX_REPORT_STANDARD_LEADER_COMMA_R_PASS_VARS get_array()); |
||
1111 | } |
||
1112 | }; |
||
1113 | |||
1114 | #define DXX_VALPTRIDX_DEFINE_FACTORY(MANAGED_TYPE, GLOBAL_FACTORY, GLOBAL_ARRAY, MEMBER_FACTORY) \ |
||
1115 | __attribute_unused static auto &GLOBAL_FACTORY = GLOBAL_ARRAY.MEMBER_FACTORY |
||
1116 | |||
1117 | #define DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORY(MANAGED_TYPE, DERIVED_TYPE_PREFIX, GLOBAL_ARRAY, PISUFFIX, IVPREFIX, MCPREFIX) \ |
||
1118 | DXX_VALPTRIDX_DEFINE_FACTORY(MANAGED_TYPE, IVPREFIX ## MCPREFIX ## DERIVED_TYPE_PREFIX ## PISUFFIX, GLOBAL_ARRAY, IVPREFIX ## MCPREFIX ## PISUFFIX) |
||
1119 | |||
1120 | #define DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORIES(MANAGED_TYPE,DERIVED_TYPE_PREFIX,GLOBAL_ARRAY) \ |
||
1121 | extern MANAGED_TYPE ## _array GLOBAL_ARRAY; \ |
||
1122 | namespace { namespace { \ |
||
1123 | DXX_VALPTRIDX_FOR_EACH_PPI_TYPE(DXX_VALPTRIDX_DEFINE_GLOBAL_FACTORY, MANAGED_TYPE, DERIVED_TYPE_PREFIX, GLOBAL_ARRAY); \ |
||
1124 | } } |
||
1125 |