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 |