├── README.md ├── all_in_one.hpp ├── error.cpp ├── include ├── bit_cast.hpp ├── compiler.hpp ├── error.hpp ├── intrusive_ptr.hpp ├── launder.hpp ├── string_ref.hpp └── type_traits.hpp └── test.cpp /README.md: -------------------------------------------------------------------------------- 1 | # std_error 2 | Implementation of `std::error` as proposed by Herb Sutter in [Zero-Overhead Deterministic Exceptions](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0709r0.pdf) 3 | 4 | Requires at least C++14. Tested on GCC-4.9.2 to GCC 10, Clang 4 to Clang 10, and MSVC 19.14 to 19.24 5 | 6 | All-in-one header-only link: https://github.com/charles-salvia/std_error/blob/master/all_in_one.hpp
7 | Example usage/godbolt link: https://godbolt.org/z/8o1Yg6
8 | Full unit tests on godbolt: https://godbolt.org/z/TbuRHh
9 | 10 | Associated paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2170r0.html 11 | 12 | **Example** 13 | ```c++ 14 | // store an std::exception_ptr in an error 15 | stdx::error e = std::make_exception_ptr(std::invalid_argument{"missing pants"}); 16 | assert(e == std::errc::invalid_argument); 17 | assert(e == stdx::dynamic_exception_errc::invalid_argument); 18 | assert(e.message() == "missing pants"); 19 | assert(e.domain() == stdx::dynamic_exception_domain); 20 | 21 | try { e.throw_exception(); } 22 | catch (const std::invalid_argument& a) { assert(stdx::string_ref{a.what()} == "missing pants"); }; 23 | 24 | e = std::make_exception_ptr(std::logic_error{"abc"}); 25 | assert(e == stdx::dynamic_exception_errc::logic_error); 26 | assert(e.message() == "abc"); 27 | 28 | // store an std::errc enum 29 | e = std::errc::bad_file_descriptor; 30 | assert(e == std::errc::bad_file_descriptor); 31 | assert(e.message() == std::make_error_code(std::errc::bad_file_descriptor).message().c_str()); 32 | assert(e.domain() == stdx::generic_domain); 33 | 34 | // store an std::error_code 35 | e = std::make_error_code(std::io_errc::stream); 36 | assert(e == std::make_error_code(std::io_errc::stream)); 37 | assert(e.domain() == stdx::error_code_domain); 38 | 39 | // store an std::exception_ptr, weak-equality comparison with std::errc enum 40 | e = std::make_exception_ptr(std::system_error{std::make_error_code(std::errc::host_unreachable)}); 41 | assert(e.domain() == stdx::dynamic_exception_domain); 42 | assert(e == std::errc::host_unreachable); 43 | assert(e.message() == std::make_error_code(std::errc::host_unreachable).message().c_str()); 44 | ``` 45 | -------------------------------------------------------------------------------- /all_in_one.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_COMPILER_HPP 2 | #define STDX_COMPILER_HPP 3 | 4 | // Check compiler macros. Note that Clang defines __GNUC__ and other GNU macros as well, 5 | // but GNU does not define Clang macros, so we must check for Clang first. 6 | 7 | #if defined(__llvm__) || defined(__clang__) 8 | 9 | // -------- LLVM/Clang 10 | 11 | #define STDX_CLANG_COMPILER 1 12 | 13 | #if defined(__cpp_variable_templates) && (__cplusplus >= 201703L) 14 | #define STDX_VARIABLE_TEMPLATES 1 15 | #endif 16 | 17 | #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) 18 | 19 | // -------- GNU G++ 20 | 21 | #include 22 | 23 | #define STDX_GCC_COMPILER 1 24 | 25 | #if (__GNUC__ >= 5) && (__cplusplus >= 201703L) 26 | #define STDX_VARIABLE_TEMPLATES 1 27 | #endif 28 | 29 | #if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1))) 30 | #define STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE 1 31 | #endif 32 | 33 | #endif 34 | 35 | #if defined(STDX_GCC_COMPILER) 36 | #if (__GNUC__ == 7) && ((__GNUC_MINOR__ >= 1) && (__GNUC_MINOR__ <= 3)) 37 | #define STDX_GCC7_WORKAROUND_CONSTEXPR 38 | #else 39 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr 40 | #endif 41 | #else 42 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr 43 | #endif 44 | 45 | // Add a legacy constexpr macro for cases where GCC < 5 incorrectly applies the const 46 | // qualifier to constexpr member functions or does not support relaxed constexpr functions 47 | // 48 | #if defined(STDX_GCC_COMPILER) && (__GNUC__ < 5) 49 | #define STDX_LEGACY_CONSTEXPR 50 | #else 51 | #define STDX_LEGACY_CONSTEXPR constexpr 52 | #endif 53 | 54 | #if defined(_MSC_VER) && (_MSC_VER >= 1910) 55 | #define STDX_MSVC_EMPTY_BASE_CLASSES __declspec(empty_bases) 56 | #else 57 | #define STDX_MSVC_EMPTY_BASE_CLASSES 58 | #endif 59 | 60 | #if defined(__cpp_impl_trivially_relocatable) 61 | #define STDX_TRIVIALLY_RELOCATABLE [[trivially_relocatable]] 62 | #else 63 | #define STDX_TRIVIALLY_RELOCATABLE 64 | #endif 65 | 66 | #endif // STDX_COMPILER_HPP 67 | 68 | 69 | #ifndef STDX_TYPE_TRAITS_HPP 70 | #define STDX_TYPE_TRAITS_HPP 71 | 72 | #include 73 | 74 | namespace stdx { 75 | 76 | // Implementation of std::void_t for use with pre-C++17 compilers. 77 | // 78 | namespace detail { 79 | 80 | template 81 | struct void_t_impl 82 | { 83 | using type = void; 84 | }; 85 | 86 | } // end namespace detail 87 | 88 | template 89 | using void_t = typename detail::void_t_impl::type; 90 | 91 | template 92 | struct dependent_type 93 | { 94 | using type = U; 95 | }; 96 | 97 | template 98 | using dependent_type_t = typename dependent_type::type; 99 | 100 | template 101 | struct disjunction : std::false_type 102 | { }; 103 | 104 | template 105 | struct disjunction : B1 106 | { }; 107 | 108 | template 109 | struct disjunction 110 | : 111 | std::conditional_t< 112 | bool(B1::value), 113 | B1, 114 | disjunction 115 | > 116 | { }; 117 | 118 | struct sentinel_type 119 | { 120 | static constexpr bool value = true; 121 | using type = void; 122 | }; 123 | 124 | template 125 | struct bool_constant : std::integral_constant 126 | { }; 127 | 128 | // Implementation of std::remove_cvref for use with pre-C++20 compilers. 129 | // 130 | template 131 | struct remove_cvref 132 | { 133 | using type = std::remove_cv_t>; 134 | }; 135 | 136 | template 137 | using remove_cvref_t = typename remove_cvref::type; 138 | 139 | #if defined(STDX_GCC_COMPILER) 140 | // Support missing functionality on older compilers (<= gcc 4.7) 141 | // 142 | #if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 7) 143 | // Map the old incorrect type-trait names to the newer correct ones 144 | template 145 | using is_trivially_copyable = std::is_trivial; 146 | 147 | template 148 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor; 149 | 150 | template 151 | using is_trivially_destructible = std::has_trivial_destructor; 152 | #elif (__GNUC__ < 5) 153 | template 154 | using is_trivially_copyable = std::is_trivial; 155 | 156 | template 157 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor; 158 | 159 | template 160 | using is_trivially_destructible = std::is_trivially_destructible; 161 | #else 162 | using std::is_trivially_destructible; 163 | using std::is_trivially_copyable; 164 | using std::is_trivially_copy_constructible; 165 | #endif 166 | #else 167 | using std::is_trivially_destructible; 168 | using std::is_trivially_copyable; 169 | using std::is_trivially_copy_constructible; 170 | #endif 171 | 172 | #if defined(STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE) 173 | template 174 | using is_trivially_move_constructible = std::is_trivially_move_constructible; 175 | #else 176 | template 177 | using is_trivially_move_constructible = is_trivially_copyable; 178 | #endif 179 | 180 | #if defined(__cpp_lib_trivially_relocatable) 181 | using std::is_trivially_relocatable; 182 | #elif defined(__has_builtin) 183 | #if __has_builtin(__is_trivially_relocatable) 184 | template 185 | struct is_trivially_relocatable : std::bool_constant<__is_trivially_relocatable(T)> { }; 186 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 187 | #else 188 | template 189 | struct is_trivially_relocatable : is_trivially_copyable { }; 190 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 191 | #endif 192 | #else 193 | template 194 | struct is_trivially_relocatable : is_trivially_copyable { }; 195 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 196 | #endif 197 | 198 | #if __cplusplus >= 201703L 199 | #define STDX_LEGACY_INLINE_CONSTEXPR inline constexpr 200 | #else 201 | #define STDX_LEGACY_INLINE_CONSTEXPR constexpr 202 | #endif 203 | 204 | } // end namespace stdx 205 | 206 | #endif 207 | 208 | 209 | 210 | #ifndef STDX_BIT_CAST_HPP 211 | #define STDX_BIT_CAST_HPP 212 | 213 | #include 214 | #include 215 | 216 | 217 | namespace stdx { 218 | 219 | namespace detail { 220 | 221 | template 222 | using use_static_cast = bool_constant< 223 | ((std::is_integral::value || std::is_enum::value) 224 | && (std::is_integral::value || std::is_enum::value)) 225 | || (std::is_same::value && std::is_copy_constructible::value) 226 | >; 227 | 228 | template 229 | using is_integral_ptr_t = bool_constant< 230 | std::is_same::value 231 | || std::is_same::value 232 | >; 233 | 234 | template 235 | using use_reinterpret_cast = bool_constant< 236 | !std::is_same::value 237 | && (( 238 | std::is_pointer::value 239 | && std::is_pointer::value 240 | && std::is_convertible::value 241 | ) 242 | || (std::is_pointer::value && is_integral_ptr_t::value) 243 | || (std::is_pointer::value && is_integral_ptr_t::value) 244 | ) 245 | >; 246 | 247 | #if defined(STDX_GCC_COMPILER) 248 | template 249 | using use_union_type_punning = bool_constant< 250 | !use_static_cast::value 251 | && !use_reinterpret_cast::value 252 | && !std::is_array::value 253 | && !std::is_array::value 254 | >; 255 | 256 | template 257 | union bit_cast_union 258 | { 259 | From from; 260 | To to; 261 | }; 262 | #else 263 | template 264 | using use_union_type_punning = std::false_type; 265 | #endif 266 | 267 | template 268 | using can_bit_cast = bool_constant< 269 | (sizeof(To) == sizeof(From)) 270 | && is_trivially_copyable::value 271 | && is_trivially_copyable::value 272 | >; 273 | 274 | } // end namespace detail 275 | 276 | template < 277 | class To, 278 | class From, 279 | class = std::enable_if_t< 280 | detail::can_bit_cast::value 281 | && detail::use_static_cast::value 282 | > 283 | > 284 | constexpr To bit_cast(const From& from) noexcept 285 | { 286 | return static_cast(from); 287 | } 288 | 289 | template < 290 | class To, 291 | class From, 292 | class = std::enable_if_t< 293 | detail::can_bit_cast::value 294 | && detail::use_reinterpret_cast::value 295 | >, 296 | int = 0 297 | > 298 | constexpr To bit_cast(const From& from) noexcept 299 | { 300 | return reinterpret_cast(from); 301 | } 302 | 303 | #if defined(STDX_GCC_COMPILER) // GCC allows union type punning 304 | template < 305 | class To, 306 | class From, 307 | class = std::enable_if_t< 308 | detail::can_bit_cast::value 309 | && detail::use_union_type_punning::value 310 | >, 311 | class = void 312 | > 313 | constexpr To bit_cast(const From& from) noexcept 314 | { 315 | return detail::bit_cast_union{from}.to; 316 | } 317 | #elif defined(STDX_CLANG_COMPILER) 318 | #if __has_builtin(__builtin_bit_cast) 319 | template < 320 | class To, 321 | class From, 322 | class = std::enable_if_t< 323 | detail::can_bit_cast::value 324 | && !detail::use_static_cast::value 325 | && !detail::use_reinterpret_cast::value 326 | >, 327 | class = void 328 | > 329 | constexpr To bit_cast(const From& from) noexcept 330 | { 331 | return __builtin_bit_cast(To, from); 332 | } 333 | #else 334 | template < 335 | class To, 336 | class From, 337 | class = std::enable_if_t< 338 | detail::can_bit_cast::value 339 | && !detail::use_static_cast::value 340 | && !detail::use_reinterpret_cast::value 341 | >, 342 | class = void 343 | > 344 | To bit_cast(const From& from) noexcept 345 | { 346 | To to; 347 | std::memcpy(&to, &from, sizeof(To)); 348 | return to; 349 | } 350 | #endif 351 | #endif // STDX_CLANG_COMPILER 352 | 353 | template 354 | struct is_bit_castable 355 | : 356 | bool_constant< 357 | (sizeof(To) == sizeof(From)) 358 | && is_trivially_copyable::value 359 | && is_trivially_copyable::value 360 | > 361 | { }; 362 | 363 | } // end namespace stdx 364 | 365 | #endif 366 | 367 | 368 | 369 | #ifndef STDX_INTRUSIVE_POINTER_HPP 370 | #define STDX_INTRUSIVE_POINTER_HPP 371 | 372 | 373 | #include 374 | #include 375 | #include 376 | 377 | namespace stdx { 378 | 379 | class default_intrusive_reference_count; 380 | class default_intrusive_reference_control; 381 | 382 | template < 383 | class T, 384 | class RefCountAccessor = default_intrusive_reference_count, 385 | class Deleter = std::default_delete, 386 | class Pointer = T* 387 | > 388 | class intrusive_ptr; 389 | 390 | using ref_count_t = std::size_t; 391 | 392 | struct enable_reference_count 393 | { 394 | protected: 395 | 396 | constexpr enable_reference_count() noexcept : m_reference_count(1) 397 | { } 398 | 399 | public: 400 | 401 | std::atomic& shared_reference_count() noexcept 402 | { 403 | return m_reference_count; 404 | } 405 | 406 | private: 407 | 408 | std::atomic m_reference_count; 409 | }; 410 | 411 | struct default_intrusive_reference_count 412 | { 413 | template 414 | std::atomic& operator()(Pointer p) const noexcept 415 | { 416 | return p->shared_reference_count(); 417 | } 418 | }; 419 | 420 | namespace detail { 421 | 422 | template 423 | struct pointer_wrapper 424 | { 425 | constexpr pointer_wrapper() noexcept : shared_object(nullptr) 426 | { } 427 | 428 | constexpr explicit pointer_wrapper(Pointer p) noexcept : shared_object(p) 429 | { } 430 | 431 | constexpr pointer_wrapper(const pointer_wrapper& p) noexcept = default; 432 | 433 | pointer_wrapper(pointer_wrapper&& p) noexcept 434 | : shared_object(p.shared_object) 435 | { 436 | p.shared_object = nullptr; 437 | } 438 | 439 | pointer_wrapper& operator = (const pointer_wrapper&) noexcept = default; 440 | 441 | pointer_wrapper& operator = (pointer_wrapper&& p) noexcept 442 | { 443 | shared_object = p.shared_object; 444 | p.shared_object = nullptr; 445 | return *this; 446 | } 447 | 448 | void assign(Pointer ptr) noexcept 449 | { 450 | shared_object = ptr; 451 | } 452 | 453 | Pointer shared_object; 454 | }; 455 | 456 | template < 457 | class T, 458 | class RefCountAccessor, 459 | class Deleter, 460 | class Pointer, 461 | class PointerImplementation 462 | > 463 | class intrusive_ptr_base 464 | { 465 | protected: 466 | 467 | using pointer = Pointer; 468 | using count_type = ref_count_t; 469 | 470 | template 471 | friend class reference_count_base; 472 | 473 | constexpr intrusive_ptr_base() = default; 474 | 475 | constexpr intrusive_ptr_base(pointer p) noexcept 476 | : m_impl(p) 477 | { } 478 | 479 | template 480 | constexpr explicit intrusive_ptr_base( 481 | RefCountAccessorForwardingReference&& f, 482 | std::enable_if_t< 483 | std::is_constructible< 484 | RefCountAccessor, 485 | RefCountAccessorForwardingReference&& 486 | >::value 487 | >* = nullptr 488 | ) 489 | : m_impl(std::forward(f)) 490 | { } 491 | 492 | template 493 | explicit intrusive_ptr_base( 494 | pointer ptr, 495 | RefCountAccessorForwardingReference&& f, 496 | std::enable_if_t< 497 | std::is_constructible< 498 | RefCountAccessor, 499 | RefCountAccessorForwardingReference&& 500 | >::value 501 | >* = nullptr 502 | ) 503 | : m_impl(ptr, std::forward(f)) 504 | { } 505 | 506 | template < 507 | class RefCountAccessorForwardingReference, 508 | class DeleterForwardingReference 509 | > 510 | constexpr explicit intrusive_ptr_base( 511 | RefCountAccessorForwardingReference&& f, 512 | DeleterForwardingReference&& d, 513 | typename std::enable_if< 514 | std::is_constructible< 515 | RefCountAccessor, 516 | RefCountAccessorForwardingReference&& 517 | >::value 518 | && std::is_constructible< 519 | Deleter, 520 | DeleterForwardingReference&& 521 | >::value 522 | >::type* = nullptr 523 | ) 524 | : 525 | m_impl( 526 | std::forward(f), 527 | std::forward(d) 528 | ) 529 | { } 530 | 531 | template < 532 | class RefCountAccessorForwardingReference, 533 | class DeleterForwardingReference 534 | > 535 | explicit intrusive_ptr_base( 536 | pointer ptr, 537 | RefCountAccessorForwardingReference&& f, 538 | DeleterForwardingReference&& d, 539 | std::enable_if_t< 540 | std::is_constructible< 541 | RefCountAccessor, 542 | RefCountAccessorForwardingReference&& 543 | >::value 544 | && std::is_constructible< 545 | Deleter, 546 | DeleterForwardingReference&& 547 | >::value 548 | >* = nullptr 549 | ) 550 | : 551 | m_impl( 552 | ptr, 553 | std::forward(f), 554 | std::forward(d) 555 | ) 556 | { } 557 | 558 | void assign(pointer ptr) noexcept 559 | { 560 | m_impl.assign(ptr); 561 | } 562 | 563 | template < 564 | class RefCountAccessorForwardingReference, 565 | class DeleterForwardingReference 566 | > 567 | void assign( 568 | pointer ptr, 569 | RefCountAccessorForwardingReference&& f, 570 | DeleterForwardingReference&& d 571 | ) 572 | { 573 | m_impl.assign( 574 | ptr, 575 | std::forward(f), 576 | std::forward(d) 577 | ); 578 | } 579 | 580 | void swap(intrusive_ptr_base& other) 581 | { 582 | m_impl.swap(other.m_impl); 583 | } 584 | 585 | struct STDX_MSVC_EMPTY_BASE_CLASSES impl 586 | : 587 | PointerImplementation, 588 | RefCountAccessor, 589 | Deleter 590 | { 591 | constexpr impl() = default; 592 | 593 | explicit impl(pointer ptr) noexcept : PointerImplementation(ptr) 594 | { } 595 | 596 | template < 597 | class RefCountAccess, 598 | class = std::enable_if_t< 599 | std::is_constructible::value 600 | > 601 | > 602 | constexpr explicit impl(RefCountAccess&& f) 603 | : RefCountAccessor(std::forward(f)) 604 | { } 605 | 606 | template < 607 | class RefCountAccess, 608 | class = std::enable_if_t< 609 | std::is_constructible::value 610 | > 611 | > 612 | impl(pointer ptr, RefCountAccess&& f) 613 | : 614 | PointerImplementation(ptr), 615 | RefCountAccessor(std::forward(f)) 616 | { } 617 | 618 | template < 619 | class RefCountAccess, 620 | class D, 621 | class = std::enable_if_t< 622 | std::is_constructible::value 623 | && std::is_constructible::value 624 | > 625 | > 626 | constexpr impl(RefCountAccess&& f, D&& d) 627 | : 628 | RefCountAccessor(std::forward(f)), 629 | Deleter(std::forward(d)) 630 | { } 631 | 632 | template < 633 | class RefCountAccess, 634 | class D, 635 | class = std::enable_if_t< 636 | std::is_constructible::value 637 | && std::is_constructible::value 638 | > 639 | > 640 | impl(pointer ptr, RefCountAccess&& f, D&& d) 641 | : 642 | PointerImplementation(ptr), 643 | RefCountAccessor(std::forward(f)), 644 | Deleter(std::forward(d)) 645 | { } 646 | 647 | impl(const impl&) = default; 648 | impl& operator = (const impl&) = default; 649 | impl(impl&&) = default; 650 | impl& operator = (impl&&) = default; 651 | 652 | Deleter& get_deleter() noexcept 653 | { 654 | return static_cast(*this); 655 | } 656 | 657 | const Deleter& get_deleter() const noexcept 658 | { 659 | return static_cast(*this); 660 | } 661 | 662 | void assign(pointer ptr) noexcept 663 | { 664 | static_cast(*this).assign(ptr); 665 | } 666 | 667 | void assign(std::nullptr_t) noexcept 668 | { 669 | static_cast(*this).assign(nullptr); 670 | } 671 | 672 | void swap(impl& other) 673 | { 674 | std::swap( 675 | static_cast(*this), 676 | static_cast(other) 677 | ); 678 | 679 | std::swap(static_cast(*this), static_cast(other)); 680 | std::swap( 681 | static_cast(this->get_deleter()), 682 | static_cast(other.get_deleter()) 683 | ); 684 | } 685 | }; 686 | 687 | impl m_impl; 688 | 689 | void increment_shared_reference_count( 690 | std::memory_order order = std::memory_order_relaxed 691 | ) const noexcept 692 | { 693 | if (ptr()) ref_count_func()(ptr()).fetch_add(1, order); 694 | } 695 | 696 | void decrement_shared_reference_count() noexcept 697 | { 698 | if (ptr()) 699 | { 700 | if (ref_count_func()(ptr()).fetch_sub(1, std::memory_order_release) == 1) 701 | { 702 | std::atomic_thread_fence(std::memory_order_acquire); 703 | invoke_deleter(ptr()); 704 | } 705 | } 706 | } 707 | 708 | // ----- accessors and modifiers 709 | 710 | pointer& ptr() noexcept 711 | { 712 | return static_cast(m_impl).shared_object; 713 | } 714 | 715 | constexpr pointer ptr() const noexcept 716 | { 717 | return static_cast(m_impl).shared_object; 718 | } 719 | 720 | RefCountAccessor& ref_count_func() noexcept 721 | { 722 | return static_cast(m_impl); 723 | } 724 | 725 | const RefCountAccessor& ref_count_func() const noexcept 726 | { 727 | return static_cast(m_impl); 728 | } 729 | 730 | Deleter& deleter() noexcept 731 | { 732 | return m_impl.get_deleter(); 733 | } 734 | 735 | const Deleter& deleter() const noexcept 736 | { 737 | return m_impl.get_deleter(); 738 | } 739 | 740 | intrusive_ptr< 741 | T, 742 | RefCountAccessor, 743 | Deleter, 744 | Pointer 745 | > make_intrusive_pointer(pointer p) const noexcept 746 | { 747 | return intrusive_ptr{ 748 | p, 749 | ref_count_func(), 750 | deleter() 751 | }; 752 | } 753 | 754 | private: 755 | 756 | void invoke_deleter(pointer p) 757 | { 758 | m_impl.get_deleter()(p); 759 | } 760 | 761 | void invoke_deleter(pointer p) const 762 | { 763 | m_impl.get_deleter()(p); 764 | } 765 | 766 | template 767 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) 768 | { 769 | maybe_delete_shared_object(p, m_impl.get_deleter(), d); 770 | } 771 | 772 | template 773 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) const 774 | { 775 | maybe_delete_shared_object(p, m_impl.get_deleter(), d); 776 | } 777 | }; 778 | 779 | } // end namespace detail 780 | 781 | template < 782 | class T, 783 | class RefCountAccessor, 784 | class Deleter, 785 | class Pointer 786 | > 787 | class STDX_TRIVIALLY_RELOCATABLE intrusive_ptr 788 | : 789 | public detail::intrusive_ptr_base< 790 | T, 791 | RefCountAccessor, 792 | Deleter, 793 | Pointer, 794 | detail::pointer_wrapper 795 | > 796 | { 797 | using base_type = detail::intrusive_ptr_base< 798 | T, 799 | RefCountAccessor, 800 | Deleter, 801 | Pointer, 802 | detail::pointer_wrapper 803 | >; 804 | 805 | public: 806 | 807 | using pointer = Pointer; 808 | using element_type = T; 809 | using ref_count_accessor = RefCountAccessor; 810 | using deleter_type = Deleter; 811 | using count_type = typename base_type::count_type; 812 | 813 | constexpr intrusive_ptr() noexcept : base_type() 814 | { } 815 | 816 | constexpr intrusive_ptr(std::nullptr_t) noexcept : base_type() 817 | { } 818 | 819 | template 820 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f) 821 | : base_type(std::forward(f)) 822 | { } 823 | 824 | template 825 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f, D&& d) 826 | : base_type(std::forward(f), std::forward(d)) 827 | { } 828 | 829 | constexpr explicit intrusive_ptr(Pointer ptr) noexcept 830 | : base_type(ptr) 831 | { 832 | // reference count must initially be >= 1 833 | } 834 | 835 | template 836 | intrusive_ptr(Pointer ptr, RefCountAccess&& f) noexcept 837 | : base_type(ptr, std::forward(f)) 838 | { 839 | // reference count must initially be >= 1 840 | } 841 | 842 | template 843 | intrusive_ptr(Pointer ptr, RefCountAccess&& f, D&& d) noexcept 844 | : 845 | base_type( 846 | ptr, 847 | std::forward(f), 848 | std::forward(d) 849 | ) 850 | { 851 | // reference count must initially be >= 1 852 | } 853 | 854 | // Copy constructor 855 | // 856 | intrusive_ptr(const intrusive_ptr& rhs) noexcept 857 | : base_type(rhs) 858 | { 859 | this->increment_shared_reference_count(); 860 | } 861 | 862 | // Converting copy-constructor 863 | // 864 | template < 865 | class Y, 866 | class Ptr, 867 | class = std::enable_if_t::value> 868 | > 869 | intrusive_ptr(const intrusive_ptr& rhs) noexcept 870 | : 871 | base_type( 872 | rhs.get(), 873 | rhs.ref_count_func(), 874 | rhs.get_deleter() 875 | ) 876 | { 877 | this->increment_shared_reference_count(); 878 | } 879 | 880 | // Move constructor 881 | // 882 | intrusive_ptr(intrusive_ptr&& rhs) noexcept 883 | : base_type(std::move(rhs)) 884 | { } 885 | 886 | // Copy assignment 887 | // 888 | intrusive_ptr& operator = (const intrusive_ptr& rhs) noexcept 889 | { 890 | rhs.increment_shared_reference_count(); 891 | this->decrement_shared_reference_count(); 892 | 893 | static_cast(*this) = static_cast(rhs); 894 | return *this; 895 | } 896 | 897 | // Move assignment 898 | // 899 | intrusive_ptr& operator = (intrusive_ptr&& rhs) noexcept 900 | { 901 | if (this != std::addressof(rhs)) 902 | { 903 | this->decrement_shared_reference_count(); 904 | static_cast(*this) = std::move(static_cast(rhs)); 905 | } 906 | 907 | return *this; 908 | } 909 | 910 | ~intrusive_ptr() noexcept 911 | { 912 | this->decrement_shared_reference_count(); 913 | } 914 | 915 | void reset() noexcept 916 | { 917 | this->decrement_shared_reference_count(); 918 | this->assign(nullptr); 919 | } 920 | 921 | void reset(std::nullptr_t) noexcept 922 | { 923 | reset(); 924 | } 925 | 926 | void reset(Pointer ptr) noexcept 927 | { 928 | if (this->ptr() != ptr) 929 | { 930 | this->decrement_shared_reference_count(); 931 | this->assign(ptr); 932 | this->increment_shared_reference_count(); 933 | } 934 | } 935 | 936 | void swap(intrusive_ptr& other) noexcept 937 | { 938 | if (this->get() != other.get()) 939 | { 940 | base_type::swap(other); 941 | } 942 | } 943 | 944 | pointer get() const noexcept 945 | { 946 | return this->ptr(); 947 | } 948 | 949 | element_type& operator * () const noexcept 950 | { 951 | return *this->get(); 952 | } 953 | 954 | pointer operator -> () const noexcept 955 | { 956 | return this->get(); 957 | } 958 | 959 | count_type use_count() const noexcept 960 | { 961 | return this->ref_count_func()(get()).load(std::memory_order_acquire); 962 | } 963 | 964 | explicit operator bool() const noexcept 965 | { 966 | return static_cast(this->get()); 967 | } 968 | 969 | Deleter get_deleter() noexcept 970 | { 971 | return this->deleter(); 972 | } 973 | 974 | const Deleter& get_deleter() const noexcept 975 | { 976 | return this->deleter(); 977 | } 978 | 979 | RefCountAccessor ref_count_access() noexcept 980 | { 981 | return this->ref_count_func(); 982 | } 983 | 984 | const RefCountAccessor& ref_count_access() const noexcept 985 | { 986 | return this->ref_count_func(); 987 | } 988 | 989 | private: 990 | 991 | template 992 | friend class intrusive_ptr; 993 | }; 994 | 995 | // -------------- Global equality operators 996 | // 997 | template 998 | bool operator == ( 999 | const intrusive_ptr& lhs, 1000 | const intrusive_ptr& rhs 1001 | ) noexcept 1002 | { 1003 | return lhs.get() == rhs.get(); 1004 | } 1005 | 1006 | template 1007 | bool operator != ( 1008 | const intrusive_ptr& lhs, 1009 | const intrusive_ptr& rhs 1010 | ) noexcept 1011 | { 1012 | return !(lhs == rhs); 1013 | } 1014 | 1015 | template 1016 | bool operator < ( 1017 | const intrusive_ptr& lhs, 1018 | const intrusive_ptr& rhs 1019 | ) noexcept 1020 | { 1021 | using pointer1 = typename intrusive_ptr::pointer; 1022 | using pointer2 = typename intrusive_ptr::pointer; 1023 | using common_type = typename std::common_type::type; 1024 | 1025 | return std::less{}(lhs.get(), rhs.get()); 1026 | } 1027 | 1028 | template 1029 | bool operator > ( 1030 | const intrusive_ptr& lhs, 1031 | const intrusive_ptr& rhs 1032 | ) noexcept 1033 | { 1034 | return rhs < lhs; 1035 | } 1036 | 1037 | template 1038 | bool operator <= ( 1039 | const intrusive_ptr& lhs, 1040 | const intrusive_ptr& rhs 1041 | ) noexcept 1042 | { 1043 | return !(rhs < lhs); 1044 | } 1045 | 1046 | template 1047 | bool operator >= ( 1048 | const intrusive_ptr& lhs, 1049 | const intrusive_ptr& rhs 1050 | ) noexcept 1051 | { 1052 | return !(lhs < rhs); 1053 | } 1054 | 1055 | template 1056 | bool operator == (const intrusive_ptr& lhs, std::nullptr_t) noexcept 1057 | { 1058 | return !lhs; 1059 | } 1060 | 1061 | template 1062 | bool operator == (std::nullptr_t, const intrusive_ptr& rhs) noexcept 1063 | { 1064 | return !rhs; 1065 | } 1066 | 1067 | template 1068 | bool operator != (const intrusive_ptr& lhs, std::nullptr_t) noexcept 1069 | { 1070 | return bool(lhs); 1071 | } 1072 | 1073 | template 1074 | bool operator != (std::nullptr_t, const intrusive_ptr& rhs) noexcept 1075 | { 1076 | return bool(rhs); 1077 | } 1078 | 1079 | template 1080 | bool operator < (const intrusive_ptr& lhs, std::nullptr_t) noexcept 1081 | { 1082 | using pointer = typename intrusive_ptr::pointer; 1083 | return std::less{}(lhs.get(), nullptr); 1084 | } 1085 | 1086 | template 1087 | bool operator < (std::nullptr_t, const intrusive_ptr& rhs) noexcept 1088 | { 1089 | using pointer = typename intrusive_ptr::pointer; 1090 | return std::less{}(nullptr, rhs.get()); 1091 | } 1092 | 1093 | template 1094 | bool operator > (const intrusive_ptr& lhs, std::nullptr_t) noexcept 1095 | { 1096 | return (nullptr < lhs); 1097 | } 1098 | 1099 | template 1100 | bool operator > (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 1101 | { 1102 | return (rhs < nullptr); 1103 | } 1104 | 1105 | template 1106 | bool operator <= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept 1107 | { 1108 | return !(nullptr < lhs); 1109 | } 1110 | 1111 | template 1112 | bool operator <= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 1113 | { 1114 | return !(rhs < nullptr); 1115 | } 1116 | 1117 | template 1118 | bool operator >= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept 1119 | { 1120 | return !(lhs < nullptr); 1121 | } 1122 | 1123 | template 1124 | bool operator >= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 1125 | { 1126 | return !(nullptr < rhs); 1127 | } 1128 | 1129 | template 1130 | void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) noexcept 1131 | { 1132 | lhs.swap(rhs); 1133 | } 1134 | 1135 | #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 1136 | template 1137 | struct is_trivially_relocatable> : std::true_type 1138 | { }; 1139 | #endif 1140 | 1141 | } // end namespace stdx 1142 | 1143 | #endif // include guard 1144 | 1145 | 1146 | 1147 | #ifndef STDX_STRING_REF_HPP 1148 | #define STDX_STRING_REF_HPP 1149 | 1150 | #include 1151 | #include 1152 | #include 1153 | 1154 | namespace stdx { 1155 | 1156 | class string_ref; 1157 | 1158 | namespace detail { 1159 | 1160 | constexpr const char* cstring_null_scan(const char* s) noexcept 1161 | { 1162 | return *s ? cstring_null_scan(s + 1) : s; 1163 | } 1164 | 1165 | } // end namespace detail 1166 | 1167 | class string_ref 1168 | { 1169 | protected: 1170 | 1171 | class state_type; 1172 | 1173 | public: 1174 | 1175 | using value_type = const char; 1176 | using size_type = std::size_t; 1177 | using pointer = const char*; 1178 | using const_pointer = const char*; 1179 | using iterator = const char*; 1180 | using const_iterator = const char*; 1181 | 1182 | struct resource_management 1183 | { 1184 | using copy_constructor = state_type(*)(const string_ref&); 1185 | using move_constructor = state_type(*)(string_ref&&); 1186 | using destructor = void(*)(string_ref&); 1187 | 1188 | constexpr resource_management() noexcept 1189 | : copy{nullptr}, move{nullptr}, destroy{nullptr} 1190 | { } 1191 | 1192 | constexpr resource_management( 1193 | copy_constructor cctor, 1194 | move_constructor mctor, 1195 | destructor dtor 1196 | ) noexcept 1197 | : copy{cctor}, move{mctor}, destroy{dtor} 1198 | { } 1199 | 1200 | copy_constructor copy; 1201 | move_constructor move; 1202 | destructor destroy; 1203 | }; 1204 | 1205 | constexpr string_ref() noexcept : m_begin(nullptr), m_end(nullptr), context{} 1206 | { } 1207 | 1208 | constexpr string_ref(const char* beg) noexcept 1209 | : m_begin(beg), m_end(detail::cstring_null_scan(beg)), context{} 1210 | { } 1211 | 1212 | constexpr string_ref(const char* beg, const char* e) noexcept 1213 | : m_begin(beg), m_end(e), context{} 1214 | { } 1215 | 1216 | constexpr string_ref(const char* beg, resource_management rm) noexcept 1217 | : 1218 | m_begin(beg), 1219 | m_end(detail::cstring_null_scan(beg)), 1220 | m_resource_management(rm), 1221 | context{} 1222 | { } 1223 | 1224 | constexpr string_ref(const char* beg, const char* e, resource_management rm) noexcept 1225 | : m_begin(beg), m_end(e), m_resource_management(rm), context{} 1226 | { } 1227 | 1228 | constexpr string_ref( 1229 | const char* beg, 1230 | const char* e, 1231 | resource_management rm, 1232 | void* ctx 1233 | ) noexcept 1234 | : m_begin(beg), m_end(e), m_resource_management(rm), context{ctx} 1235 | { } 1236 | 1237 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(const string_ref& s) 1238 | : string_ref{s.m_resource_management.copy ? s.m_resource_management.copy(s) : s.state()} 1239 | { } 1240 | 1241 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(string_ref&& s) 1242 | : 1243 | string_ref{ 1244 | s.m_resource_management.move ? 1245 | s.m_resource_management.move(std::move(s)) : s.state() 1246 | } 1247 | { } 1248 | 1249 | string_ref& operator = (const string_ref& s) 1250 | { 1251 | string_ref tmp = s; 1252 | *this = std::move(tmp); 1253 | return *this; 1254 | } 1255 | 1256 | string_ref& operator = (string_ref&& s) 1257 | { 1258 | if (this != &s) 1259 | { 1260 | if (m_resource_management.destroy) m_resource_management.destroy(*this); 1261 | 1262 | // This is legal because of the common initial sequence and the fact 1263 | // that any type erased object must be trivially relocatable. 1264 | *this = string_ref_state_union{std::move(s)}.state; 1265 | } 1266 | 1267 | return *this; 1268 | } 1269 | 1270 | ~string_ref() noexcept 1271 | { 1272 | if (m_resource_management.destroy) m_resource_management.destroy(*this); 1273 | } 1274 | 1275 | bool empty() const noexcept { return m_begin == m_end; } 1276 | 1277 | size_type size() const noexcept { return m_end - m_begin; } 1278 | 1279 | const_pointer data() const noexcept { return m_begin; } 1280 | 1281 | iterator begin() noexcept { return m_begin; } 1282 | 1283 | iterator end() noexcept { return m_end; } 1284 | 1285 | const_iterator begin() const noexcept { return m_begin; } 1286 | 1287 | const_iterator end() const noexcept { return m_end; } 1288 | 1289 | const_iterator cbegin() const noexcept { return m_begin; } 1290 | 1291 | const_iterator cend() const noexcept { return m_end; } 1292 | 1293 | protected: 1294 | 1295 | struct state_type 1296 | { 1297 | pointer m_begin; 1298 | pointer m_end; 1299 | resource_management m_resource_management; 1300 | void* context; 1301 | }; 1302 | 1303 | state_type state() const noexcept 1304 | { 1305 | return state_type{m_begin, m_end, m_resource_management, context}; 1306 | } 1307 | 1308 | constexpr explicit string_ref(const state_type& s) noexcept 1309 | : 1310 | m_begin(s.m_begin), 1311 | m_end(s.m_end), 1312 | m_resource_management(s.m_resource_management), 1313 | context(s.context) 1314 | { } 1315 | 1316 | void clear() noexcept 1317 | { 1318 | m_begin = nullptr; 1319 | m_end = nullptr; 1320 | } 1321 | 1322 | template 1323 | union string_ref_state_union_type 1324 | { 1325 | explicit string_ref_state_union_type(StringRef&& s) : str(std::move(s)) 1326 | { } 1327 | 1328 | ~string_ref_state_union_type() noexcept {} 1329 | 1330 | StringRef str; 1331 | state_type state; 1332 | }; 1333 | 1334 | using string_ref_state_union = string_ref_state_union_type; 1335 | 1336 | void operator = (const state_type& s) noexcept 1337 | { 1338 | m_begin = s.m_begin; 1339 | m_end = s.m_end; 1340 | m_resource_management = s.m_resource_management; 1341 | context = s.context; 1342 | } 1343 | 1344 | pointer m_begin; 1345 | pointer m_end; 1346 | resource_management m_resource_management; 1347 | void* context; 1348 | }; 1349 | 1350 | inline bool operator == (const string_ref& lhs, const string_ref& rhs) noexcept 1351 | { 1352 | return (lhs.size() == rhs.size()) && (std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0); 1353 | } 1354 | 1355 | inline bool operator != (const string_ref& lhs, const string_ref& rhs) noexcept 1356 | { 1357 | return !(lhs == rhs); 1358 | } 1359 | 1360 | inline bool operator < (const string_ref& lhs, const string_ref& rhs) noexcept 1361 | { 1362 | const std::size_t sz = (lhs.size() < rhs.size()) ? lhs.size() : rhs.size(); 1363 | int result = std::memcmp(lhs.data(), rhs.data(), sz); 1364 | if (result == 0) return lhs.size() < rhs.size(); 1365 | return result < 0; 1366 | } 1367 | 1368 | inline bool operator > (const string_ref& lhs, const string_ref& rhs) noexcept 1369 | { 1370 | return rhs < lhs; 1371 | } 1372 | 1373 | inline bool operator <= (const string_ref& lhs, const string_ref& rhs) noexcept 1374 | { 1375 | return !(lhs > rhs); 1376 | } 1377 | 1378 | inline bool operator >= (const string_ref& lhs, const string_ref& rhs) noexcept 1379 | { 1380 | return !(lhs < rhs); 1381 | } 1382 | 1383 | // Reference-counted allocated string 1384 | // 1385 | class shared_string_ref : public string_ref 1386 | { 1387 | struct string_arena_base 1388 | { 1389 | mutable std::atomic ref_count; 1390 | std::size_t length; 1391 | }; 1392 | 1393 | struct string_arena : string_arena_base 1394 | { 1395 | constexpr explicit string_arena(std::size_t length) noexcept 1396 | : string_arena_base{{1}, length} 1397 | { } 1398 | 1399 | constexpr static std::size_t header_size() noexcept 1400 | { 1401 | return sizeof(string_arena); 1402 | } 1403 | 1404 | char* data() noexcept 1405 | { 1406 | return reinterpret_cast(this) + header_size(); 1407 | } 1408 | 1409 | const char* data() const noexcept 1410 | { 1411 | return reinterpret_cast(this) + header_size(); 1412 | } 1413 | 1414 | const char* begin() const noexcept { return data(); } 1415 | 1416 | const char* end() const noexcept { return data() + length; } 1417 | }; 1418 | 1419 | string_ref allocate_string_ref(const char* s, std::size_t length) 1420 | { 1421 | const std::size_t arena_size = string_arena::header_size() + length; 1422 | char* buf = static_cast(::operator new(arena_size)); 1423 | string_arena* a = new (buf) string_arena{length}; 1424 | std::memcpy(a->data(), s, length); 1425 | return shared_string_ref{a}; 1426 | } 1427 | 1428 | explicit shared_string_ref(string_arena* a) noexcept 1429 | : 1430 | string_ref{ 1431 | a->begin(), 1432 | a->end(), 1433 | string_ref::resource_management{©_construct, &move_construct, &destroy}, 1434 | a 1435 | } 1436 | { } 1437 | 1438 | const string_arena_base* get_arena() const noexcept 1439 | { 1440 | return static_cast(this->context); 1441 | } 1442 | 1443 | string_arena_base* get_arena() noexcept 1444 | { 1445 | return static_cast(this->context); 1446 | } 1447 | 1448 | static string_ref::state_type copy_construct(const string_ref& base) noexcept 1449 | { 1450 | const shared_string_ref& s = static_cast(base); 1451 | const string_arena_base* a = s.get_arena(); 1452 | if (a) a->ref_count.fetch_add(1, std::memory_order_relaxed); 1453 | return s.state(); 1454 | } 1455 | 1456 | static string_ref::state_type move_construct(string_ref&& base) noexcept 1457 | { 1458 | shared_string_ref& s = static_cast(base); 1459 | auto st = s.state(); 1460 | s.context = nullptr; 1461 | s.clear(); 1462 | return st; 1463 | } 1464 | 1465 | static void destroy(string_ref& base) noexcept 1466 | { 1467 | shared_string_ref& s = static_cast(base); 1468 | string_arena* a = static_cast(s.get_arena()); 1469 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1)) 1470 | { 1471 | std::atomic_thread_fence(std::memory_order_acquire); 1472 | ::operator delete(a); 1473 | } 1474 | } 1475 | 1476 | template 1477 | struct allocated_string_arena : string_arena_base 1478 | { 1479 | constexpr allocated_string_arena(const Allocator& alloc, std::size_t length) noexcept 1480 | : string_arena_base{{1}, length}, allocator(alloc) 1481 | { } 1482 | 1483 | constexpr static std::size_t header_size() noexcept 1484 | { 1485 | return sizeof(allocated_string_arena); 1486 | } 1487 | 1488 | char* data() noexcept 1489 | { 1490 | return reinterpret_cast(this) + header_size(); 1491 | } 1492 | 1493 | const char* data() const noexcept 1494 | { 1495 | return reinterpret_cast(this) + header_size(); 1496 | } 1497 | 1498 | const char* begin() const noexcept { return data(); } 1499 | 1500 | const char* end() const noexcept { return data() + length; } 1501 | 1502 | std::size_t allocated_size() const noexcept { return header_size() + length; } 1503 | 1504 | Allocator allocator; 1505 | }; 1506 | 1507 | template 1508 | explicit shared_string_ref(allocated_string_arena* a) noexcept 1509 | : 1510 | string_ref{ 1511 | a->begin(), 1512 | a->end(), 1513 | string_ref::resource_management{ 1514 | ©_construct, 1515 | &move_construct, 1516 | &allocator_destroy 1517 | }, 1518 | a 1519 | } 1520 | { } 1521 | 1522 | template 1523 | string_ref allocate_string_ref( 1524 | const Allocator& allocator, 1525 | const char* s, 1526 | std::size_t length 1527 | ) 1528 | { 1529 | using allocator_type = typename std::allocator_traits< 1530 | Allocator 1531 | >::template rebind_alloc; 1532 | using arena_type = allocated_string_arena; 1533 | 1534 | allocator_type alloc{allocator}; 1535 | const std::size_t arena_size = arena_type::header_size() + length; 1536 | char* buf = alloc.allocate(arena_size); 1537 | arena_type* a = new (buf) arena_type{alloc, length}; 1538 | std::memcpy(a->data(), s, length); 1539 | return shared_string_ref{a}; 1540 | } 1541 | 1542 | template 1543 | static void allocator_destroy(string_ref& base) noexcept 1544 | { 1545 | using arena_type = allocated_string_arena; 1546 | 1547 | shared_string_ref& s = static_cast(base); 1548 | arena_type* a = static_cast(s.get_arena()); 1549 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1)) 1550 | { 1551 | std::atomic_thread_fence(std::memory_order_acquire); 1552 | 1553 | Allocator alloc = std::move(a->allocator); 1554 | const std::size_t allocated_size = a->allocated_size(); 1555 | a->~arena_type(); 1556 | alloc.deallocate(reinterpret_cast(a), allocated_size); 1557 | } 1558 | } 1559 | 1560 | public: 1561 | 1562 | shared_string_ref(const char* beg) 1563 | : string_ref{allocate_string_ref(beg, detail::cstring_null_scan(beg) - beg)} 1564 | { } 1565 | 1566 | shared_string_ref(const char* beg, const char* end) 1567 | : string_ref{allocate_string_ref(beg, end - beg)} 1568 | { } 1569 | 1570 | template 1571 | shared_string_ref(const Allocator& alloc, const char* beg) 1572 | : string_ref{allocate_string_ref(alloc, beg, detail::cstring_null_scan(beg) - beg)} 1573 | { } 1574 | 1575 | template 1576 | shared_string_ref(const Allocator& alloc, const char* beg, const char* end) 1577 | : string_ref{allocate_string_ref(alloc, beg, end - beg)} 1578 | { } 1579 | 1580 | std::size_t use_count() const noexcept 1581 | { 1582 | const string_arena_base* a = get_arena(); 1583 | return a ? a->ref_count.load(std::memory_order_acquire) : 0; 1584 | } 1585 | }; 1586 | 1587 | } // end namespace stdx 1588 | 1589 | #endif 1590 | 1591 | 1592 | 1593 | #ifndef STDX_LAUNDER_HPP 1594 | #define STDX_LAUNDER_HPP 1595 | 1596 | #include 1597 | 1598 | 1599 | namespace stdx { 1600 | 1601 | #if __cplusplus >= 201703L 1602 | #if defined(__cpp_lib_launder) 1603 | #define STDX_HAVE_NATIVE_LAUNDER 1 1604 | using std::launder; 1605 | #elif defined(STDX_CLANG_COMPILER) 1606 | #if __has_builtin(__builtin_launder) 1607 | #define STDX_HAVE_NATIVE_LAUNDER 1 1608 | template 1609 | constexpr T* launder(T* p) noexcept 1610 | { 1611 | return __builtin_launder(p); 1612 | } 1613 | #endif 1614 | #endif 1615 | #endif 1616 | 1617 | #if !defined(STDX_HAVE_NATIVE_LAUNDER) 1618 | template 1619 | constexpr T* launder(T* p) noexcept 1620 | { 1621 | return p; 1622 | } 1623 | #endif 1624 | 1625 | } // end namespace stdx 1626 | 1627 | #endif 1628 | 1629 | 1630 | 1631 | #ifndef STDX_ERROR_HPP 1632 | #define STDX_ERROR_HPP 1633 | 1634 | #include 1635 | #include 1636 | #include 1637 | #include 1638 | #include 1639 | 1640 | 1641 | namespace stdx { 1642 | 1643 | class error; 1644 | 1645 | namespace detail { 1646 | 1647 | template 1648 | struct error_ctor_args; 1649 | 1650 | } // end namespace detail 1651 | 1652 | } // end namespace stdx 1653 | 1654 | namespace stdx_adl { 1655 | 1656 | namespace detail { 1657 | 1658 | template 1659 | struct can_use_adl_to_call_make_error : std::false_type 1660 | { }; 1661 | 1662 | inline void make_error() noexcept { } 1663 | 1664 | template 1665 | struct can_use_adl_to_call_make_error< 1666 | stdx::detail::error_ctor_args, 1667 | std::enable_if_t< 1668 | std::is_same< 1669 | decltype(make_error(std::declval()...)), 1670 | stdx::error 1671 | >::value 1672 | > 1673 | > 1674 | : std::true_type 1675 | { }; 1676 | 1677 | template 1678 | constexpr auto construct_error_from_adl(Args&&... args) noexcept( 1679 | noexcept(make_error(std::forward(args)...)) 1680 | ) 1681 | { 1682 | return make_error(std::forward(args)...); 1683 | } 1684 | } 1685 | 1686 | } // end namespace stdx_adl 1687 | 1688 | namespace stdx { 1689 | 1690 | enum class dynamic_exception_errc 1691 | { 1692 | runtime_error = 1, 1693 | domain_error, 1694 | invalid_argument, 1695 | length_error, 1696 | out_of_range, 1697 | logic_error, 1698 | range_error, 1699 | overflow_error, 1700 | underflow_error, 1701 | bad_alloc, 1702 | bad_array_new_length, 1703 | bad_optional_access, 1704 | bad_typeid, 1705 | bad_any_cast, 1706 | bad_cast, 1707 | bad_weak_ptr, 1708 | bad_function_call, 1709 | bad_exception, 1710 | bad_variant_access, 1711 | unspecified_exception 1712 | }; 1713 | 1714 | std::error_code error_code_from_exception(std::exception_ptr eptr) noexcept; 1715 | 1716 | // -------------------- error_traits 1717 | // 1718 | template 1719 | struct error_traits 1720 | { }; 1721 | 1722 | namespace detail { 1723 | 1724 | template 1725 | struct is_convertible_from_exception_using_traits : std::false_type 1726 | { }; 1727 | 1728 | template 1729 | struct is_convertible_from_exception_using_traits< 1730 | E, 1731 | std::enable_if_t< 1732 | std::is_convertible< 1733 | decltype(error_traits::from_exception(std::declval())), 1734 | E 1735 | >::value 1736 | > 1737 | > 1738 | : std::true_type 1739 | { }; 1740 | 1741 | template 1742 | struct is_convertible_to_exception_using_traits : std::false_type 1743 | { }; 1744 | 1745 | template 1746 | struct is_convertible_to_exception_using_traits< 1747 | E, 1748 | std::enable_if_t< 1749 | std::is_convertible< 1750 | decltype(error_traits::to_exception(std::declval())), 1751 | std::exception_ptr 1752 | >::value 1753 | > 1754 | > 1755 | : std::true_type 1756 | { }; 1757 | 1758 | template 1759 | E from_exception_impl(std::exception_ptr e, std::is_convertible) noexcept 1760 | { 1761 | return e; 1762 | } 1763 | 1764 | template 1765 | E from_exception_impl( 1766 | std::exception_ptr e, 1767 | is_convertible_from_exception_using_traits 1768 | ) noexcept 1769 | { 1770 | return error_traits::from_exception(std::move(e)); 1771 | } 1772 | 1773 | void from_exception_impl(std::exception_ptr, sentinel_type) = delete; 1774 | 1775 | template 1776 | std::exception_ptr to_exception_impl(E&& e, std::is_convertible) noexcept 1777 | { 1778 | return std::forward(e); 1779 | } 1780 | 1781 | template 1782 | std::exception_ptr to_exception_impl( 1783 | E&& e, 1784 | is_convertible_to_exception_using_traits 1785 | ) noexcept 1786 | { 1787 | return error_traits::to_exception(std::forward(e)); 1788 | } 1789 | 1790 | template 1791 | void to_exception_impl(const E&, sentinel_type) = delete; 1792 | 1793 | } // end namespace detail 1794 | 1795 | template 1796 | E from_exception(std::exception_ptr e) noexcept 1797 | { 1798 | return detail::from_exception_impl( 1799 | std::move(e), 1800 | disjunction< 1801 | std::is_convertible, 1802 | detail::is_convertible_from_exception_using_traits, 1803 | sentinel_type 1804 | >{} 1805 | ); 1806 | } 1807 | 1808 | template 1809 | std::exception_ptr to_exception(E&& e) noexcept 1810 | { 1811 | return detail::to_exception_impl( 1812 | std::forward(e), 1813 | disjunction< 1814 | std::is_convertible, std::exception_ptr>, 1815 | detail::is_convertible_to_exception_using_traits>, 1816 | sentinel_type 1817 | >{} 1818 | ); 1819 | } 1820 | 1821 | struct error_domain_id 1822 | { 1823 | constexpr error_domain_id(std::uint64_t l, std::uint64_t h) noexcept 1824 | : lo(l), hi(h) 1825 | { } 1826 | 1827 | private: 1828 | 1829 | friend constexpr bool operator == (const error_domain_id&, const error_domain_id&) noexcept; 1830 | 1831 | std::uint64_t lo; 1832 | std::uint64_t hi; 1833 | }; 1834 | 1835 | constexpr bool operator == (const error_domain_id& lhs, const error_domain_id& rhs) noexcept 1836 | { 1837 | return (lhs.lo == rhs.lo) && (lhs.hi == rhs.hi); 1838 | } 1839 | 1840 | constexpr bool operator != (const error_domain_id& lhs, const error_domain_id& rhs) noexcept 1841 | { 1842 | return !(lhs == rhs); 1843 | } 1844 | 1845 | template 1846 | struct error_value; 1847 | 1848 | namespace detail { 1849 | 1850 | template 1851 | struct is_error_value : std::false_type 1852 | { }; 1853 | 1854 | template 1855 | struct is_error_value> : std::true_type 1856 | { }; 1857 | 1858 | } // end namespace detail 1859 | 1860 | struct error_resource_management 1861 | { 1862 | using copy_constructor = error_value(*)(const error&); 1863 | using move_constructor = error_value(*)(error&&); 1864 | using destructor = void(*)(error&); 1865 | 1866 | constexpr error_resource_management() noexcept 1867 | : copy{nullptr}, move{nullptr}, destroy{nullptr} 1868 | { } 1869 | 1870 | constexpr error_resource_management( 1871 | copy_constructor cctor, 1872 | move_constructor mctor, 1873 | destructor dtor 1874 | ) noexcept 1875 | : copy(cctor), move(mctor), destroy(dtor) 1876 | { } 1877 | 1878 | copy_constructor copy; 1879 | move_constructor move; 1880 | destructor destroy; 1881 | }; 1882 | 1883 | class error_domain 1884 | { 1885 | public: 1886 | 1887 | virtual string_ref name() const noexcept = 0; 1888 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept = 0; 1889 | virtual string_ref message(const error&) const noexcept = 0; 1890 | 1891 | virtual void throw_exception(const error& e) const; 1892 | 1893 | friend class error; 1894 | friend constexpr bool operator == (const error_domain&, const error_domain&) noexcept; 1895 | friend constexpr bool operator != (const error_domain&, const error_domain&) noexcept; 1896 | 1897 | constexpr error_domain_id id() const noexcept 1898 | { 1899 | return m_id; 1900 | } 1901 | 1902 | protected: 1903 | 1904 | constexpr explicit error_domain(error_domain_id id) noexcept 1905 | : 1906 | m_id{id}, 1907 | m_resource_management{} 1908 | { } 1909 | 1910 | constexpr error_domain(error_domain_id id, error_resource_management erm) noexcept 1911 | : 1912 | m_id{id}, 1913 | m_resource_management{erm} 1914 | { } 1915 | 1916 | error_domain(const error_domain &) = default; 1917 | error_domain(error_domain &&) = default; 1918 | error_domain &operator = (const error_domain &) = default; 1919 | error_domain &operator = (error_domain&&) = default; 1920 | ~error_domain() = default; 1921 | 1922 | template < 1923 | class E = error, 1924 | class = std::enable_if_t< 1925 | std::is_convertible::value 1926 | > 1927 | > 1928 | constexpr dependent_type_t> copy(const E& e) const 1929 | { 1930 | return m_resource_management.copy ? m_resource_management.copy(e) : error_value<>{e.m_value}; 1931 | } 1932 | 1933 | template < 1934 | class E = error, 1935 | class = std::enable_if_t< 1936 | std::is_rvalue_reference::value 1937 | && std::is_convertible::value 1938 | > 1939 | > 1940 | constexpr dependent_type_t, error_value<>> move(E&& e) const 1941 | { 1942 | return m_resource_management.move ? 1943 | m_resource_management.move(static_cast(e)) : error_value<>{e.m_value}; 1944 | } 1945 | 1946 | void destroy(error& e) const noexcept 1947 | { 1948 | if (m_resource_management.destroy) m_resource_management.destroy(e); 1949 | } 1950 | 1951 | private: 1952 | 1953 | error_domain_id m_id; 1954 | error_resource_management m_resource_management; 1955 | }; 1956 | 1957 | constexpr bool operator == (const error_domain& lhs, const error_domain& rhs) noexcept 1958 | { 1959 | return lhs.id() == rhs.id(); 1960 | } 1961 | 1962 | constexpr bool operator != (const error_domain& lhs, const error_domain& rhs) noexcept 1963 | { 1964 | return lhs.id() != rhs.id(); 1965 | } 1966 | 1967 | namespace detail { 1968 | 1969 | template 1970 | struct error_type_is_erasable 1971 | : 1972 | bool_constant< 1973 | is_trivially_relocatable::value 1974 | && (sizeof(T) <= sizeof(ErasedType)) 1975 | && (alignof(T) <= alignof(ErasedType)) 1976 | > 1977 | { }; 1978 | 1979 | template 1980 | struct error_type_is_erasable : std::false_type 1981 | { }; 1982 | 1983 | template 1984 | struct error_type_is_erasable : std::false_type 1985 | { }; 1986 | 1987 | template 1988 | using can_use_static_cast = bool_constant< 1989 | std::is_integral::value 1990 | || std::is_enum::value 1991 | >; 1992 | 1993 | struct erased_error 1994 | { 1995 | using integral_type = std::intptr_t; 1996 | using storage_type = std::aligned_storage_t; 1997 | 1998 | constexpr erased_error() noexcept : code{} 1999 | { } 2000 | 2001 | template < 2002 | class T, 2003 | class = std::enable_if_t< 2004 | error_type_is_erasable::value 2005 | && can_use_static_cast::value 2006 | > 2007 | > 2008 | constexpr erased_error(T value) noexcept 2009 | : code{static_cast(value)} 2010 | { } 2011 | 2012 | template < 2013 | class T, 2014 | class = std::enable_if_t< 2015 | error_type_is_erasable::value 2016 | && !can_use_static_cast::value 2017 | && is_bit_castable::value 2018 | >, 2019 | class = void 2020 | > 2021 | constexpr erased_error(T value) noexcept 2022 | : code{bit_cast(value)} 2023 | { } 2024 | 2025 | template < 2026 | class T, 2027 | class = std::enable_if_t< 2028 | error_type_is_erasable::value 2029 | && !can_use_static_cast::value 2030 | && !is_bit_castable::value 2031 | >, 2032 | int = 0 2033 | > 2034 | erased_error(T value) noexcept(std::is_nothrow_move_constructible::value) 2035 | { 2036 | new (&storage) T(std::move(value)); 2037 | } 2038 | 2039 | union 2040 | { 2041 | integral_type code; 2042 | storage_type storage; 2043 | }; 2044 | }; 2045 | 2046 | template < 2047 | class T, 2048 | class = std::enable_if_t< 2049 | error_type_is_erasable::value 2050 | && can_use_static_cast::value 2051 | > 2052 | > 2053 | constexpr T error_cast_impl(erased_error e) noexcept 2054 | { 2055 | return static_cast(e.code); 2056 | } 2057 | 2058 | template < 2059 | class T, 2060 | class = std::enable_if_t< 2061 | error_type_is_erasable::value 2062 | && !can_use_static_cast::value 2063 | && is_bit_castable::value 2064 | >, 2065 | class = void 2066 | > 2067 | constexpr T error_cast_impl(erased_error e) noexcept 2068 | { 2069 | return bit_cast(e.code); 2070 | } 2071 | 2072 | template < 2073 | class T, 2074 | class = std::enable_if_t< 2075 | error_type_is_erasable::value 2076 | && !can_use_static_cast::value 2077 | && !is_bit_castable::value 2078 | > 2079 | > 2080 | constexpr T error_cast_impl(erased_error&& e) noexcept( 2081 | std::is_nothrow_move_constructible::value 2082 | ) 2083 | { 2084 | return std::move(*stdx::launder(reinterpret_cast(&e.storage))); 2085 | } 2086 | 2087 | template < 2088 | class T, 2089 | class = std::enable_if_t< 2090 | error_type_is_erasable::value 2091 | && !can_use_static_cast::value 2092 | && !is_bit_castable::value 2093 | > 2094 | > 2095 | constexpr T error_cast_impl(const erased_error& e) noexcept( 2096 | std::is_nothrow_copy_constructible::value 2097 | ) 2098 | { 2099 | return *stdx::launder(reinterpret_cast(&e.storage)); 2100 | } 2101 | 2102 | } // end namespace detail 2103 | 2104 | template 2105 | struct error_value 2106 | { 2107 | using value_type = T; 2108 | 2109 | constexpr error_value(const T& v) noexcept( 2110 | std::is_nothrow_copy_constructible::value 2111 | ) 2112 | : m_value(v) 2113 | { } 2114 | 2115 | constexpr error_value(T&& v) noexcept( 2116 | std::is_nothrow_move_constructible::value 2117 | ) 2118 | : m_value(std::move(v)) 2119 | { } 2120 | 2121 | constexpr const T& value() const & noexcept 2122 | { 2123 | return m_value; 2124 | } 2125 | 2126 | STDX_LEGACY_CONSTEXPR T& value() & noexcept 2127 | { 2128 | return m_value; 2129 | } 2130 | 2131 | constexpr const T&& value() const && noexcept 2132 | { 2133 | return static_cast(m_value); 2134 | } 2135 | 2136 | STDX_LEGACY_CONSTEXPR T&& value() && noexcept 2137 | { 2138 | return static_cast(m_value); 2139 | } 2140 | 2141 | T m_value; 2142 | }; 2143 | 2144 | template <> 2145 | struct error_value 2146 | { 2147 | template < 2148 | class T, 2149 | class = std::enable_if_t< 2150 | !std::is_same, error_value>::value 2151 | && !detail::is_error_value>::value 2152 | && std::is_constructible::value 2153 | > 2154 | > 2155 | constexpr error_value(T&& v) noexcept( 2156 | std::is_nothrow_constructible::value 2157 | ) 2158 | : m_value(std::forward(v)) 2159 | { } 2160 | 2161 | template < 2162 | class T, 2163 | class = std::enable_if_t< 2164 | std::is_constructible::value 2165 | > 2166 | > 2167 | constexpr error_value(const error_value& v) noexcept( 2168 | std::is_nothrow_constructible::value 2169 | ) 2170 | : error_value(v.value()) 2171 | { } 2172 | 2173 | template < 2174 | class T, 2175 | class = std::enable_if_t< 2176 | std::is_constructible::value 2177 | > 2178 | > 2179 | constexpr error_value(error_value&& v) noexcept( 2180 | std::is_nothrow_constructible::value 2181 | ) 2182 | : error_value(std::move(v.value())) 2183 | { } 2184 | 2185 | friend class error; 2186 | 2187 | private: 2188 | 2189 | detail::erased_error m_value; 2190 | }; 2191 | 2192 | class error; 2193 | 2194 | namespace detail { 2195 | 2196 | struct error_copy_construct_t {}; 2197 | struct error_move_construct_t {}; 2198 | 2199 | template 2200 | struct error_ctor_args {}; 2201 | 2202 | template 2203 | struct can_construct_error_from_adl 2204 | { }; 2205 | 2206 | template 2207 | struct can_construct_error_from_adl, error_ctor_args> 2208 | : stdx_adl::detail::can_use_adl_to_call_make_error> 2209 | { }; 2210 | 2211 | template <> 2212 | struct can_construct_error_from_adl, error_ctor_args<>> : std::false_type 2213 | { }; 2214 | 2215 | template 2216 | struct can_construct_error_from_adl, error_ctor_args> 2217 | : std::false_type 2218 | { }; 2219 | 2220 | template 2221 | struct can_construct_error_from_adl< 2222 | error_ctor_args, 2223 | error_ctor_args, ErrorDomain> 2224 | > 2225 | : std::false_type 2226 | { }; 2227 | 2228 | template 2229 | struct can_construct_error_from_adl< 2230 | error_ctor_args, 2231 | error_ctor_args, ErrorDomain> 2232 | > 2233 | : std::false_type 2234 | { }; 2235 | 2236 | template 2237 | struct can_construct_error_from_adl< 2238 | error_ctor_args, 2239 | error_ctor_args, ErrorDomain> 2240 | > 2241 | : std::false_type 2242 | { }; 2243 | 2244 | template 2245 | struct is_convertible_to_error_using_traits : std::false_type 2246 | { }; 2247 | 2248 | template <> 2249 | struct is_convertible_to_error_using_traits> 2250 | : std::false_type 2251 | { }; 2252 | 2253 | template 2254 | struct is_convertible_to_error_using_traits< 2255 | error_ctor_args, 2256 | std::enable_if_t< 2257 | std::is_same< 2258 | decltype(error_traits::to_error(std::declval())), 2259 | error 2260 | >::value 2261 | > 2262 | > 2263 | : std::true_type 2264 | { }; 2265 | 2266 | template 2267 | constexpr auto construct_error_impl( 2268 | is_convertible_to_error_using_traits>, 2269 | Args&&... args 2270 | ) noexcept(noexcept(error_traits::to_error(std::declval()...))) 2271 | { 2272 | return error_traits::to_error(std::forward(args)...); 2273 | } 2274 | 2275 | template 2276 | constexpr auto construct_error_impl( 2277 | can_construct_error_from_adl, 2278 | Args&&... args 2279 | ) noexcept(noexcept(stdx_adl::detail::construct_error_from_adl(std::declval()...))) 2280 | { 2281 | return stdx_adl::detail::construct_error_from_adl(std::forward(args)...); 2282 | } 2283 | 2284 | struct cannot_construct_error 2285 | { 2286 | static constexpr bool value = false; 2287 | using type = void; 2288 | }; 2289 | 2290 | template 2291 | void construct_error_impl(cannot_construct_error, Args&&...) = delete; 2292 | 2293 | template 2294 | using construct_error_disjunction_impl_t = disjunction< 2295 | is_convertible_to_error_using_traits, 2296 | can_construct_error_from_adl, 2297 | cannot_construct_error 2298 | >; 2299 | 2300 | template 2301 | using construct_error_disjunction_t = construct_error_disjunction_impl_t< 2302 | error_ctor_args, 2303 | error_ctor_args...> 2304 | >; 2305 | 2306 | struct error_move_access; 2307 | struct error_ref_access; 2308 | struct error_cref_access; 2309 | 2310 | } // end namespace detail 2311 | 2312 | // Generic domain for std::errc codes 2313 | // 2314 | class generic_error_domain : public error_domain 2315 | { 2316 | public: 2317 | 2318 | constexpr generic_error_domain() noexcept 2319 | : error_domain{{0x574ce0d940b64a2bULL, 0xa7c4438dd858c9cfULL}} 2320 | { } 2321 | 2322 | virtual string_ref name() const noexcept override 2323 | { 2324 | return "generic domain"; 2325 | } 2326 | 2327 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 2328 | 2329 | virtual string_ref message(const error&) const noexcept override; 2330 | }; 2331 | 2332 | STDX_LEGACY_INLINE_CONSTEXPR generic_error_domain generic_domain {}; 2333 | 2334 | class STDX_TRIVIALLY_RELOCATABLE error 2335 | { 2336 | using erased_type = detail::erased_error; 2337 | 2338 | constexpr error(detail::error_copy_construct_t, error_value<> v, const error_domain* d) noexcept 2339 | : m_domain(d), m_value(v.m_value) 2340 | { } 2341 | 2342 | constexpr error(detail::error_move_construct_t, error_value<> v, const error_domain* d) noexcept 2343 | : m_domain(d), m_value(std::move(v.m_value)) 2344 | { } 2345 | 2346 | public: 2347 | 2348 | constexpr error() noexcept : m_domain(&generic_domain), m_value{} 2349 | { } 2350 | 2351 | constexpr error(const error& e) 2352 | : error(detail::error_copy_construct_t{}, e.m_domain->copy(e), e.m_domain) 2353 | { } 2354 | 2355 | constexpr error(error&& e) 2356 | : error(detail::error_move_construct_t{}, e.m_domain->move(std::move(e)), e.m_domain) 2357 | { } 2358 | 2359 | template < 2360 | class T, 2361 | class = std::enable_if_t< 2362 | detail::error_type_is_erasable::value 2363 | > 2364 | > 2365 | constexpr error(const error_value& v, const error_domain& d) noexcept 2366 | : m_domain(&d), m_value(v.value()) 2367 | { } 2368 | 2369 | template < 2370 | class T, 2371 | class = std::enable_if_t< 2372 | detail::error_type_is_erasable::value 2373 | > 2374 | > 2375 | constexpr error(error_value&& v, const error_domain& d) noexcept 2376 | : m_domain(&d), m_value(static_cast(v.value())) 2377 | { } 2378 | 2379 | constexpr error(error_value<> v, const error_domain& d) noexcept 2380 | : m_domain(&d), m_value(v.m_value) 2381 | { } 2382 | 2383 | template < 2384 | class A, 2385 | class... Args, 2386 | class = std::enable_if_t< 2387 | detail::construct_error_disjunction_t::value 2388 | > 2389 | > 2390 | constexpr error(A&& a, Args&&... args) noexcept( 2391 | noexcept( 2392 | detail::construct_error_impl( 2393 | std::declval>(), 2394 | std::forward(a), 2395 | std::forward(args)... 2396 | ) 2397 | ) 2398 | ) 2399 | : 2400 | error( 2401 | detail::construct_error_impl( 2402 | detail::construct_error_disjunction_t{}, 2403 | std::forward(a), 2404 | std::forward(args)... 2405 | ) 2406 | ) 2407 | { } 2408 | 2409 | error& operator = (const error& e) 2410 | { 2411 | error_value<> v = e.domain().copy(e); 2412 | domain().destroy(*this); 2413 | m_domain = e.m_domain; 2414 | m_value = v.m_value; 2415 | return *this; 2416 | } 2417 | 2418 | error& operator = (error&& e) noexcept 2419 | { 2420 | if (this != &e) 2421 | { 2422 | error_value<> v = e.domain().move(std::move(e)); 2423 | domain().destroy(*this); 2424 | m_domain = e.m_domain; 2425 | m_value = v.m_value; 2426 | } 2427 | 2428 | return *this; 2429 | } 2430 | 2431 | ~error() noexcept 2432 | { 2433 | m_domain->destroy(*this); 2434 | } 2435 | 2436 | const error_domain& domain() const noexcept 2437 | { 2438 | return *m_domain; 2439 | } 2440 | 2441 | string_ref message() const noexcept 2442 | { 2443 | return domain().message(*this); 2444 | } 2445 | 2446 | [[noreturn]] void throw_exception() const 2447 | { 2448 | domain().throw_exception(*this); 2449 | abort(); 2450 | } 2451 | 2452 | friend class error_domain; 2453 | friend struct detail::error_move_access; 2454 | friend struct detail::error_ref_access; 2455 | friend struct detail::error_cref_access; 2456 | 2457 | private: 2458 | 2459 | const error_domain* m_domain; 2460 | erased_type m_value; 2461 | }; 2462 | 2463 | inline bool operator == (const error& lhs, const error& rhs) noexcept 2464 | { 2465 | if (lhs.domain().equivalent(lhs, rhs)) return true; 2466 | if (rhs.domain().equivalent(rhs, lhs)) return true; 2467 | return false; 2468 | } 2469 | 2470 | inline bool operator != (const error& lhs, const error& rhs) noexcept 2471 | { 2472 | return !(lhs == rhs); 2473 | } 2474 | 2475 | namespace detail { 2476 | 2477 | struct error_move_access 2478 | { 2479 | constexpr explicit error_move_access(error&& e) noexcept : m_value(&e.m_value) 2480 | { } 2481 | 2482 | STDX_LEGACY_CONSTEXPR detail::erased_error&& rvalue_ref() noexcept 2483 | { 2484 | return std::move(*m_value); 2485 | } 2486 | 2487 | detail::erased_error* m_value; 2488 | }; 2489 | 2490 | struct error_ref_access 2491 | { 2492 | constexpr explicit error_ref_access(error& e) noexcept : m_ptr(&e.m_value) 2493 | { } 2494 | 2495 | detail::erased_error& ref() noexcept { return *m_ptr; } 2496 | 2497 | detail::erased_error* m_ptr; 2498 | }; 2499 | 2500 | struct error_cref_access 2501 | { 2502 | constexpr explicit error_cref_access(const error& e) noexcept : m_ptr(&e.m_value) 2503 | { } 2504 | 2505 | constexpr const detail::erased_error& ref() const noexcept { return *m_ptr; } 2506 | 2507 | const detail::erased_error* m_ptr; 2508 | }; 2509 | 2510 | } // end namespace detail 2511 | 2512 | template < 2513 | class T, 2514 | class = void_t< 2515 | decltype(detail::error_cast_impl(std::declval())) 2516 | > 2517 | > 2518 | constexpr T error_cast(const error& e) noexcept( 2519 | noexcept(detail::error_cast_impl(std::declval())) 2520 | ) 2521 | { 2522 | return detail::error_cast_impl(detail::error_cref_access{e}.ref()); 2523 | } 2524 | 2525 | template < 2526 | class T, 2527 | class = void_t(std::declval()))> 2528 | > 2529 | constexpr T error_cast(error&& e) noexcept( 2530 | noexcept(detail::error_cast_impl(std::declval())) 2531 | ) 2532 | { 2533 | return detail::error_cast_impl(detail::error_move_access{std::move(e)}.rvalue_ref()); 2534 | } 2535 | 2536 | namespace detail { 2537 | 2538 | struct default_error_constructors 2539 | { 2540 | template 2541 | static error_value<> copy_constructor(const error& e) noexcept( 2542 | std::is_nothrow_copy_constructible::value 2543 | && std::is_nothrow_move_constructible::value 2544 | ) 2545 | { 2546 | T value = error_cast(e); 2547 | return error_value<>{std::move(value)}; 2548 | } 2549 | 2550 | template 2551 | static error_value<> move_constructor(error&& e) noexcept( 2552 | std::is_nothrow_move_constructible::value 2553 | ) 2554 | { 2555 | return error_value<>{error_cast(std::move(e))}; 2556 | } 2557 | 2558 | template 2559 | static void destructor(error& e) noexcept 2560 | { 2561 | detail::erased_error& value = error_ref_access{e}.ref(); 2562 | stdx::launder(reinterpret_cast(&value.storage))->~T(); 2563 | } 2564 | 2565 | template 2566 | constexpr static error_resource_management::copy_constructor copy() noexcept 2567 | { 2568 | return is_trivially_copy_constructible::value ? 2569 | nullptr : ©_constructor; 2570 | } 2571 | 2572 | template 2573 | constexpr static error_resource_management::move_constructor move() noexcept 2574 | { 2575 | return is_trivially_move_constructible::value ? 2576 | nullptr : &move_constructor; 2577 | } 2578 | 2579 | template 2580 | constexpr static error_resource_management::destructor destroy() noexcept 2581 | { 2582 | return is_trivially_destructible::value ? 2583 | nullptr : &destructor; 2584 | } 2585 | }; 2586 | 2587 | } // end namespace detail 2588 | 2589 | template 2590 | struct default_error_resource_management_t : error_resource_management 2591 | { 2592 | constexpr default_error_resource_management_t() noexcept 2593 | : 2594 | error_resource_management{ 2595 | detail::default_error_constructors::copy(), 2596 | detail::default_error_constructors::move(), 2597 | detail::default_error_constructors::destroy() 2598 | } 2599 | {} 2600 | }; 2601 | 2602 | #if defined(STDX_VARIABLE_TEMPLATES) 2603 | template 2604 | inline constexpr default_error_resource_management_t default_error_resource_management {}; 2605 | #endif 2606 | 2607 | template <> 2608 | struct error_traits 2609 | { 2610 | static std::exception_ptr to_exception(std::errc ec) noexcept 2611 | { 2612 | return std::make_exception_ptr(std::make_error_code(ec)); 2613 | } 2614 | 2615 | static error to_error(std::errc ec) noexcept 2616 | { 2617 | return error{error_value{ec}, generic_domain}; 2618 | } 2619 | }; 2620 | 2621 | namespace detail { 2622 | 2623 | struct error_code_wrapper : enable_reference_count 2624 | { 2625 | explicit error_code_wrapper(std::error_code ec) noexcept : code(ec) 2626 | { } 2627 | 2628 | std::error_code code; 2629 | }; 2630 | 2631 | } // end namespace detail 2632 | 2633 | // Error domain mapping to std::error_code 2634 | // 2635 | class error_code_error_domain : public error_domain 2636 | { 2637 | using internal_value_type = intrusive_ptr; 2638 | 2639 | friend class error_traits; 2640 | 2641 | public: 2642 | 2643 | constexpr error_code_error_domain() noexcept 2644 | : 2645 | error_domain{ 2646 | {0x84e99cdcecae4443ULL, 0x9050179b713fd2afULL}, 2647 | default_error_resource_management_t{} 2648 | } 2649 | { } 2650 | 2651 | virtual string_ref name() const noexcept override 2652 | { 2653 | return "std::error_code error domain"; 2654 | } 2655 | 2656 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 2657 | 2658 | virtual string_ref message(const error& e) const noexcept override; 2659 | 2660 | [[noreturn]] virtual void throw_exception(const error& e) const override; 2661 | }; 2662 | 2663 | STDX_LEGACY_INLINE_CONSTEXPR error_code_error_domain error_code_domain {}; 2664 | 2665 | template <> 2666 | struct error_traits 2667 | { 2668 | static std::error_code from_exception(std::exception_ptr e) noexcept 2669 | { 2670 | return error_code_from_exception(std::move(e)); 2671 | } 2672 | 2673 | static std::exception_ptr to_exception(std::error_code ec) noexcept 2674 | { 2675 | return std::make_exception_ptr(std::system_error{ec}); 2676 | } 2677 | 2678 | static error to_error(std::error_code ec) noexcept; 2679 | }; 2680 | 2681 | namespace detail { 2682 | 2683 | template 2684 | struct exception_ptr_wrapper_impl 2685 | { 2686 | struct control_block : enable_reference_count 2687 | { 2688 | explicit control_block(Ptr p) noexcept : ptr_(std::move(p)) 2689 | { } 2690 | 2691 | Ptr ptr_; 2692 | }; 2693 | 2694 | explicit exception_ptr_wrapper_impl(Ptr p) : ptr{new control_block{std::move(p)}} 2695 | { } 2696 | 2697 | Ptr get() noexcept { return ptr ? ptr->ptr_ : Ptr{}; } 2698 | 2699 | intrusive_ptr ptr; 2700 | }; 2701 | 2702 | template 2703 | struct exception_ptr_wrapper_impl 2704 | { 2705 | explicit exception_ptr_wrapper_impl(Ptr p) : ptr{std::move(p)} 2706 | { } 2707 | 2708 | Ptr get() noexcept { return ptr; } 2709 | 2710 | Ptr ptr; 2711 | }; 2712 | 2713 | using exception_ptr_wrapper = exception_ptr_wrapper_impl; 2714 | 2715 | static_assert(sizeof(exception_ptr_wrapper) == sizeof(std::intptr_t), "Internal library error"); 2716 | 2717 | } // end namespace detail 2718 | 2719 | #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 2720 | template <> 2721 | struct is_trivially_relocatable : std::true_type 2722 | { }; 2723 | #endif 2724 | 2725 | // Error domain mapping to std::exception_ptr 2726 | // 2727 | class dynamic_exception_error_domain : public error_domain 2728 | { 2729 | public: 2730 | 2731 | constexpr dynamic_exception_error_domain() noexcept 2732 | : 2733 | error_domain{ 2734 | {0x3c223c0aa3cf45e5ULL, 0x80dac24345cfb9fcULL}, 2735 | default_error_resource_management_t{} 2736 | } 2737 | { } 2738 | 2739 | virtual string_ref name() const noexcept override 2740 | { 2741 | return "dynamic exception domain"; 2742 | } 2743 | 2744 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 2745 | 2746 | virtual string_ref message(const error&) const noexcept override; 2747 | 2748 | [[noreturn]] virtual void throw_exception(const error& e) const override 2749 | { 2750 | assert(e.domain() == *this); 2751 | std::rethrow_exception(error_cast(e).get()); 2752 | } 2753 | }; 2754 | 2755 | STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_error_domain dynamic_exception_domain {}; 2756 | 2757 | // Error domain mapping to dynamic_exception_errc 2758 | // 2759 | class dynamic_exception_code_error_domain : public error_domain 2760 | { 2761 | public: 2762 | 2763 | constexpr dynamic_exception_code_error_domain() noexcept 2764 | : error_domain{{0xa242506c26484677ULL, 0x82365303df25e338ULL}} 2765 | { } 2766 | 2767 | virtual string_ref name() const noexcept override 2768 | { 2769 | return "dynamic exception code domain"; 2770 | } 2771 | 2772 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 2773 | 2774 | virtual string_ref message(const error&) const noexcept override; 2775 | }; 2776 | 2777 | STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_code_error_domain dynamic_exception_code_domain {}; 2778 | 2779 | inline error make_error(dynamic_exception_errc code) noexcept 2780 | { 2781 | return error{error_value{code}, dynamic_exception_code_domain}; 2782 | } 2783 | 2784 | struct thrown_dynamic_exception : std::exception 2785 | { 2786 | explicit thrown_dynamic_exception(stdx::error e) noexcept : m_error(e) 2787 | { } 2788 | 2789 | stdx::error error() const noexcept 2790 | { 2791 | return m_error; 2792 | } 2793 | 2794 | private: 2795 | 2796 | stdx::error m_error; 2797 | }; 2798 | 2799 | template <> 2800 | struct error_traits 2801 | { 2802 | static std::exception_ptr from_exception(std::exception_ptr e) noexcept 2803 | { 2804 | return e; 2805 | } 2806 | 2807 | static std::exception_ptr to_exception(std::exception_ptr e) noexcept 2808 | { 2809 | return e; 2810 | } 2811 | 2812 | static error to_error(std::exception_ptr e) noexcept 2813 | { 2814 | return error{ 2815 | error_value{detail::exception_ptr_wrapper{e}}, 2816 | dynamic_exception_domain 2817 | }; 2818 | } 2819 | }; 2820 | 2821 | } // end namespace stdx 2822 | 2823 | namespace std { 2824 | 2825 | template<> 2826 | struct is_error_code_enum : std::true_type 2827 | { }; 2828 | 2829 | } // end namespace std 2830 | 2831 | #endif 2832 | 2833 | 2834 | 2835 | #if __cplusplus >= 201703L 2836 | #include 2837 | #include 2838 | #include 2839 | #endif 2840 | 2841 | #include 2842 | 2843 | namespace stdx { 2844 | 2845 | namespace { 2846 | 2847 | inline const char* dynamic_exception_errc_str(unsigned ev) noexcept 2848 | { 2849 | constexpr const char* msg[] = 2850 | { 2851 | "Success", 2852 | "std::runtime_error", 2853 | "std::domain_error", 2854 | "std::invalid_argument", 2855 | "std::length_error", 2856 | "std::out_of_range", 2857 | "std::logic_error", 2858 | "std::range_error", 2859 | "std::overflow_error", 2860 | "std::underflow_error", 2861 | "std::bad_alloc", 2862 | "std::bad_array_new_length", 2863 | "std::bad_optional_access", 2864 | "std::bad_typeid", 2865 | "std::bad_any_cast", 2866 | "std::bad_cast", 2867 | "std::bad_weak_ptr", 2868 | "std::bad_function_call", 2869 | "std::bad_exception", 2870 | "std::bad_variant_access", 2871 | "unspecified dynamic exception" 2872 | }; 2873 | 2874 | assert(ev < (sizeof(msg) / sizeof(const char*))); 2875 | return msg[ev]; 2876 | } 2877 | 2878 | class dynamic_exception_error_category : public std::error_category 2879 | { 2880 | public: 2881 | 2882 | const char* name() const noexcept override 2883 | { 2884 | return "dynamic_exception"; 2885 | } 2886 | 2887 | std::string message(int code) const override 2888 | { 2889 | return dynamic_exception_errc_str(code); 2890 | } 2891 | 2892 | bool equivalent(int code, const std::error_condition& cond) const noexcept override 2893 | { 2894 | switch (static_cast(code)) 2895 | { 2896 | case dynamic_exception_errc::domain_error: 2897 | return (cond == std::errc::argument_out_of_domain); 2898 | case dynamic_exception_errc::invalid_argument: 2899 | return (cond == std::errc::invalid_argument); 2900 | case dynamic_exception_errc::length_error: 2901 | return (cond == std::errc::value_too_large); 2902 | case dynamic_exception_errc::out_of_range: 2903 | case dynamic_exception_errc::range_error: 2904 | case dynamic_exception_errc::underflow_error: 2905 | return (cond == std::errc::result_out_of_range); 2906 | case dynamic_exception_errc::overflow_error: 2907 | return (cond == std::errc::value_too_large); 2908 | case dynamic_exception_errc::bad_alloc: 2909 | case dynamic_exception_errc::bad_array_new_length: 2910 | return (cond == std::errc::not_enough_memory); 2911 | default:; 2912 | } 2913 | return false; 2914 | } 2915 | }; 2916 | 2917 | inline const std::error_category& dynamic_exception_category() noexcept 2918 | { 2919 | static const dynamic_exception_error_category dynamic_exception_error_category_instance; 2920 | return dynamic_exception_error_category_instance; 2921 | } 2922 | 2923 | } // end anonymous namespace 2924 | 2925 | inline std::error_code make_error_code(dynamic_exception_errc code) noexcept 2926 | { 2927 | return std::error_code{static_cast(code), dynamic_exception_category()}; 2928 | } 2929 | 2930 | inline std::error_code error_code_from_exception(std::exception_ptr eptr) noexcept 2931 | { 2932 | if (!eptr) return make_error_code(dynamic_exception_errc::bad_exception); 2933 | 2934 | try 2935 | { 2936 | std::rethrow_exception(eptr); 2937 | } 2938 | catch (const std::domain_error&) 2939 | { 2940 | return make_error_code(dynamic_exception_errc::domain_error); 2941 | } 2942 | catch (const std::invalid_argument&) 2943 | { 2944 | return make_error_code(dynamic_exception_errc::invalid_argument); 2945 | } 2946 | catch (const std::length_error&) 2947 | { 2948 | return make_error_code(dynamic_exception_errc::length_error); 2949 | } 2950 | catch (const std::out_of_range&) 2951 | { 2952 | return make_error_code(dynamic_exception_errc::out_of_range); 2953 | } 2954 | catch (const std::logic_error&) 2955 | { 2956 | return make_error_code(dynamic_exception_errc::logic_error); 2957 | } 2958 | catch (const std::range_error&) 2959 | { 2960 | return make_error_code(dynamic_exception_errc::range_error); 2961 | } 2962 | catch (const std::overflow_error&) 2963 | { 2964 | return make_error_code(dynamic_exception_errc::overflow_error); 2965 | } 2966 | catch (const std::underflow_error&) 2967 | { 2968 | return make_error_code(dynamic_exception_errc::underflow_error); 2969 | } 2970 | catch (const std::system_error& e) 2971 | { 2972 | return e.code(); 2973 | } 2974 | catch (const std::runtime_error&) 2975 | { 2976 | return make_error_code(dynamic_exception_errc::runtime_error); 2977 | } 2978 | catch (const std::bad_array_new_length&) 2979 | { 2980 | return make_error_code(dynamic_exception_errc::bad_array_new_length); 2981 | } 2982 | catch (const std::bad_alloc&) 2983 | { 2984 | return make_error_code(dynamic_exception_errc::bad_alloc); 2985 | } 2986 | catch (const std::bad_typeid&) 2987 | { 2988 | return make_error_code(dynamic_exception_errc::bad_typeid); 2989 | } 2990 | #if __cplusplus >= 201703L 2991 | catch (const std::bad_optional_access&) 2992 | { 2993 | return make_error_code(dynamic_exception_errc::bad_optional_access); 2994 | } 2995 | catch (const std::bad_any_cast&) 2996 | { 2997 | return make_error_code(dynamic_exception_errc::bad_any_cast); 2998 | } 2999 | catch (const std::bad_variant_access&) 3000 | { 3001 | return make_error_code(dynamic_exception_errc::bad_variant_access); 3002 | } 3003 | #endif 3004 | catch (const std::bad_cast&) 3005 | { 3006 | return make_error_code(dynamic_exception_errc::bad_cast); 3007 | } 3008 | catch (const std::bad_weak_ptr&) 3009 | { 3010 | return make_error_code(dynamic_exception_errc::bad_weak_ptr); 3011 | } 3012 | catch (const std::bad_function_call&) 3013 | { 3014 | return make_error_code(dynamic_exception_errc::bad_function_call); 3015 | } 3016 | catch (const std::bad_exception&) 3017 | { 3018 | return make_error_code(dynamic_exception_errc::bad_exception); 3019 | } 3020 | catch (...) 3021 | { } 3022 | 3023 | return make_error_code(dynamic_exception_errc::unspecified_exception); 3024 | } 3025 | 3026 | inline error error_from_exception(std::exception_ptr eptr) noexcept 3027 | { 3028 | if (!eptr) return make_error(dynamic_exception_errc::bad_exception); 3029 | 3030 | try 3031 | { 3032 | std::rethrow_exception(eptr); 3033 | } 3034 | catch (const std::domain_error&) 3035 | { 3036 | return make_error(dynamic_exception_errc::domain_error); 3037 | } 3038 | catch (const std::invalid_argument&) 3039 | { 3040 | return make_error(dynamic_exception_errc::invalid_argument); 3041 | } 3042 | catch (const std::length_error&) 3043 | { 3044 | return make_error(dynamic_exception_errc::length_error); 3045 | } 3046 | catch (const std::out_of_range&) 3047 | { 3048 | return make_error(dynamic_exception_errc::out_of_range); 3049 | } 3050 | catch (const std::logic_error&) 3051 | { 3052 | return make_error(dynamic_exception_errc::logic_error); 3053 | } 3054 | catch (const std::range_error&) 3055 | { 3056 | return make_error(dynamic_exception_errc::range_error); 3057 | } 3058 | catch (const std::overflow_error&) 3059 | { 3060 | return make_error(dynamic_exception_errc::overflow_error); 3061 | } 3062 | catch (const std::underflow_error&) 3063 | { 3064 | return make_error(dynamic_exception_errc::underflow_error); 3065 | } 3066 | catch (const std::system_error& e) 3067 | { 3068 | return error{e.code()}; 3069 | } 3070 | catch (const std::runtime_error&) 3071 | { 3072 | return make_error(dynamic_exception_errc::runtime_error); 3073 | } 3074 | catch (const std::bad_array_new_length&) 3075 | { 3076 | return make_error(dynamic_exception_errc::bad_array_new_length); 3077 | } 3078 | catch (const std::bad_alloc&) 3079 | { 3080 | return make_error(dynamic_exception_errc::bad_alloc); 3081 | } 3082 | catch (const std::bad_typeid&) 3083 | { 3084 | return make_error(dynamic_exception_errc::bad_typeid); 3085 | } 3086 | #if __cplusplus >= 201703L 3087 | catch (const std::bad_optional_access&) 3088 | { 3089 | return make_error(dynamic_exception_errc::bad_optional_access); 3090 | } 3091 | catch (const std::bad_any_cast&) 3092 | { 3093 | return make_error(dynamic_exception_errc::bad_any_cast); 3094 | } 3095 | catch (const std::bad_variant_access&) 3096 | { 3097 | return make_error(dynamic_exception_errc::bad_variant_access); 3098 | } 3099 | #endif 3100 | catch (const std::bad_cast&) 3101 | { 3102 | return make_error(dynamic_exception_errc::bad_cast); 3103 | } 3104 | catch (const std::bad_weak_ptr&) 3105 | { 3106 | return make_error(dynamic_exception_errc::bad_weak_ptr); 3107 | } 3108 | catch (const std::bad_function_call&) 3109 | { 3110 | return make_error(dynamic_exception_errc::bad_function_call); 3111 | } 3112 | catch (const std::bad_exception&) 3113 | { 3114 | return make_error(dynamic_exception_errc::bad_exception); 3115 | } 3116 | catch (...) 3117 | { } 3118 | 3119 | return make_error(dynamic_exception_errc::unspecified_exception); 3120 | } 3121 | 3122 | // ---------- ErrorDomain (abstract base class) 3123 | // 3124 | inline void error_domain::throw_exception(const error& e) const 3125 | { 3126 | throw thrown_dynamic_exception{e}; 3127 | } 3128 | 3129 | // ---------- GenericErrorDomain 3130 | // 3131 | inline bool generic_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 3132 | { 3133 | assert(lhs.domain() == *this); 3134 | if (lhs.domain() == rhs.domain()) 3135 | { 3136 | return error_cast(lhs) == error_cast(rhs); 3137 | } 3138 | 3139 | return false; 3140 | } 3141 | 3142 | namespace { 3143 | 3144 | string_ref generic_error_code_message(std::errc code) noexcept 3145 | { 3146 | switch (code) 3147 | { 3148 | case std::errc::address_family_not_supported: 3149 | return "Address family not supported by protocol"; 3150 | case std::errc::address_in_use: 3151 | return "Address already in use"; 3152 | case std::errc::address_not_available: 3153 | return "Cannot assign requested address"; 3154 | case std::errc::already_connected: 3155 | return "Transport endpoint is already connected"; 3156 | case std::errc::argument_list_too_long: 3157 | return "Argument list too long"; 3158 | case std::errc::argument_out_of_domain: 3159 | return "Numerical argument out of domain"; 3160 | case std::errc::bad_address: 3161 | return "Bad address"; 3162 | case std::errc::bad_file_descriptor: 3163 | return "Bad file descriptor"; 3164 | case std::errc::bad_message: 3165 | return "Bad message"; 3166 | case std::errc::broken_pipe: 3167 | return "Broken pipe"; 3168 | case std::errc::connection_aborted: 3169 | return "Software caused connection abort"; 3170 | case std::errc::connection_already_in_progress: 3171 | return "Operation already in progress"; 3172 | case std::errc::connection_refused: 3173 | return "Connection refused"; 3174 | case std::errc::connection_reset: 3175 | return "Connection reset by peer"; 3176 | case std::errc::cross_device_link: 3177 | return "Invalid cross-device link"; 3178 | case std::errc::destination_address_required: 3179 | return "Destination address required"; 3180 | case std::errc::device_or_resource_busy: 3181 | return "Device or resource busy"; 3182 | case std::errc::directory_not_empty: 3183 | return "Directory not empty"; 3184 | case std::errc::executable_format_error: 3185 | return "Exec format error"; 3186 | case std::errc::file_exists: 3187 | return "File exists"; 3188 | case std::errc::file_too_large: 3189 | return "File too large"; 3190 | case std::errc::filename_too_long: 3191 | return "File name too long"; 3192 | case std::errc::function_not_supported: 3193 | return "Function not implemented"; 3194 | case std::errc::host_unreachable: 3195 | return "No route to host"; 3196 | case std::errc::identifier_removed: 3197 | return "Identifier removed"; 3198 | case std::errc::illegal_byte_sequence: 3199 | return "Invalid or incomplete multibyte or wide character"; 3200 | case std::errc::inappropriate_io_control_operation: 3201 | return "Inappropriate ioctl for device"; 3202 | case std::errc::interrupted: 3203 | return "Interrupted system call"; 3204 | case std::errc::invalid_argument: 3205 | return "Invalid argument"; 3206 | case std::errc::invalid_seek: 3207 | return "Illegal seek"; 3208 | case std::errc::io_error: 3209 | return "Input/output error"; 3210 | case std::errc::is_a_directory: 3211 | return "Is a directory"; 3212 | case std::errc::message_size: 3213 | return "Message too long"; 3214 | case std::errc::network_down: 3215 | return "Network is down"; 3216 | case std::errc::network_reset: 3217 | return "Network dropped connection on reset"; 3218 | case std::errc::network_unreachable: 3219 | return "Network is unreachable"; 3220 | case std::errc::no_buffer_space: 3221 | return "No buffer space available"; 3222 | case std::errc::no_child_process: 3223 | return "No child processes"; 3224 | case std::errc::no_link: 3225 | return "Link has been severed"; 3226 | case std::errc::no_lock_available: 3227 | return "No locks available"; 3228 | case std::errc::no_message: 3229 | return "No message of desired type"; 3230 | case std::errc::no_protocol_option: 3231 | return "Protocol not available"; 3232 | case std::errc::no_space_on_device: 3233 | return "No space left on device"; 3234 | case std::errc::no_stream_resources: 3235 | return "Out of streams resources"; 3236 | case std::errc::no_such_device_or_address: 3237 | return "No such device or address"; 3238 | case std::errc::no_such_device: 3239 | return "No such device"; 3240 | case std::errc::no_such_file_or_directory: 3241 | return "No such file or directory"; 3242 | case std::errc::no_such_process: 3243 | return "No such process"; 3244 | case std::errc::not_a_directory: 3245 | return "Not a directory"; 3246 | case std::errc::not_a_socket: 3247 | return "Socket operation on non-socket"; 3248 | case std::errc::not_a_stream: 3249 | return "Device not a stream"; 3250 | case std::errc::not_connected: 3251 | return "Transport endpoint is not connected"; 3252 | case std::errc::not_enough_memory: 3253 | return "Cannot allocate memory"; 3254 | #if ENOTSUP != EOPNOTSUPP 3255 | case std::errc::not_supported: 3256 | return "Operation not supported"; 3257 | #endif 3258 | case std::errc::operation_canceled: 3259 | return "Operation canceled"; 3260 | case std::errc::operation_in_progress: 3261 | return "Operation now in progress"; 3262 | case std::errc::operation_not_permitted: 3263 | return "Operation not permitted"; 3264 | case std::errc::operation_not_supported: 3265 | return "Operation not supported"; 3266 | #if EAGAIN != EWOULDBLOCK 3267 | case std::errc::operation_would_block: 3268 | return "Resource temporarily unavailable"; 3269 | #endif 3270 | case std::errc::owner_dead: 3271 | return "Owner died"; 3272 | case std::errc::permission_denied: 3273 | return "Permission denied"; 3274 | case std::errc::protocol_error: 3275 | return "Protocol error"; 3276 | case std::errc::protocol_not_supported: 3277 | return "Protocol not supported"; 3278 | case std::errc::read_only_file_system: 3279 | return "Read-only file system"; 3280 | case std::errc::resource_deadlock_would_occur: 3281 | return "Resource deadlock avoided"; 3282 | case std::errc::resource_unavailable_try_again: 3283 | return "Resource temporarily unavailable"; 3284 | case std::errc::result_out_of_range: 3285 | return "Numerical result out of range"; 3286 | case std::errc::state_not_recoverable: 3287 | return "State not recoverable"; 3288 | case std::errc::stream_timeout: 3289 | return "Timer expired"; 3290 | case std::errc::text_file_busy: 3291 | return "Text file busy"; 3292 | case std::errc::timed_out: 3293 | return "Connection timed out"; 3294 | case std::errc::too_many_files_open_in_system: 3295 | return "Too many open files in system"; 3296 | case std::errc::too_many_files_open: 3297 | return "Too many open files"; 3298 | case std::errc::too_many_links: 3299 | return "Too many links"; 3300 | case std::errc::too_many_symbolic_link_levels: 3301 | return "Too many levels of symbolic links"; 3302 | case std::errc::value_too_large: 3303 | return "Value too large for defined data type"; 3304 | case std::errc::wrong_protocol_type: 3305 | return "Protocol wrong type for socket"; 3306 | default: 3307 | return "Unspecified error"; 3308 | } 3309 | } 3310 | 3311 | } // end anonymous namespace 3312 | 3313 | inline string_ref generic_error_domain::message(const error& e) const noexcept 3314 | { 3315 | assert(e.domain() == *this); 3316 | return generic_error_code_message(error_cast(e)); 3317 | } 3318 | 3319 | // ---------- ErrorCodeErrorDomain 3320 | // 3321 | inline string_ref error_code_error_domain::message(const error& e) const noexcept 3322 | { 3323 | assert(e.domain() == *this); 3324 | 3325 | auto ptr = error_cast(e); 3326 | if (ptr) 3327 | { 3328 | std::string msg = ptr->code.message(); 3329 | return shared_string_ref{msg.c_str(), msg.c_str() + msg.size()}; 3330 | } 3331 | 3332 | return string_ref{"Bad error code"}; 3333 | } 3334 | 3335 | inline void error_code_error_domain::throw_exception(const error& e) const 3336 | { 3337 | assert(e.domain() == *this); 3338 | 3339 | std::error_code code; 3340 | auto ptr = error_cast(e); 3341 | if (ptr) code = ptr->code; 3342 | throw std::system_error{code}; 3343 | } 3344 | 3345 | inline bool error_code_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 3346 | { 3347 | assert(lhs.domain() == *this); 3348 | 3349 | if (lhs.domain() == rhs.domain()) 3350 | { 3351 | auto ptr1 = error_cast(lhs); 3352 | auto ptr2 = error_cast(rhs); 3353 | if (ptr1 && ptr2) return ptr1->code == ptr2->code.default_error_condition(); 3354 | return false; 3355 | } 3356 | 3357 | if (rhs.domain() == generic_domain) 3358 | { 3359 | auto ptr1 = error_cast(lhs); 3360 | if (ptr1) return ptr1->code == error_cast(rhs); 3361 | } 3362 | 3363 | return false; 3364 | } 3365 | 3366 | inline stdx::error error_traits::to_error(std::error_code ec) noexcept 3367 | { 3368 | using internal_value_type = error_code_error_domain::internal_value_type; 3369 | 3370 | if (ec.category() == std::generic_category()) 3371 | { 3372 | return error{ 3373 | error_value{static_cast(ec.default_error_condition().value())}, 3374 | generic_domain 3375 | }; 3376 | } 3377 | 3378 | return error{ 3379 | error_value{internal_value_type{new detail::error_code_wrapper{ec}}}, 3380 | error_code_domain 3381 | }; 3382 | } 3383 | 3384 | // ---------- DynamicExceptionErrorDomain 3385 | // 3386 | inline string_ref dynamic_exception_error_domain::message(const error& e) const noexcept 3387 | { 3388 | assert(e.domain() == *this); 3389 | 3390 | std::exception_ptr eptr = error_cast(e).get(); 3391 | 3392 | try 3393 | { 3394 | std::rethrow_exception(eptr); 3395 | } 3396 | catch (const std::exception& ex) 3397 | { 3398 | return shared_string_ref{ex.what()}; 3399 | } 3400 | catch (...) {} 3401 | 3402 | return string_ref{"Unknown dynamic exception"}; 3403 | } 3404 | 3405 | namespace { 3406 | 3407 | std::errc dynamic_exception_code_to_generic_code(dynamic_exception_errc code) noexcept 3408 | { 3409 | switch (code) 3410 | { 3411 | case dynamic_exception_errc::domain_error: 3412 | return std::errc::argument_out_of_domain; 3413 | case dynamic_exception_errc::invalid_argument: 3414 | return std::errc::invalid_argument; 3415 | case dynamic_exception_errc::length_error: 3416 | return std::errc::value_too_large; 3417 | case dynamic_exception_errc::out_of_range: 3418 | case dynamic_exception_errc::range_error: 3419 | case dynamic_exception_errc::underflow_error: 3420 | return std::errc::result_out_of_range; 3421 | case dynamic_exception_errc::overflow_error: 3422 | return std::errc::value_too_large; 3423 | case dynamic_exception_errc::bad_alloc: 3424 | case dynamic_exception_errc::bad_array_new_length: 3425 | return std::errc::not_enough_memory; 3426 | default:; 3427 | } 3428 | return std::errc{}; 3429 | } 3430 | 3431 | } // end anonymous namespace 3432 | 3433 | inline bool dynamic_exception_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 3434 | { 3435 | assert(lhs.domain() == *this); 3436 | 3437 | std::exception_ptr eptr = error_cast(lhs).get(); 3438 | 3439 | if (rhs.domain() == *this) 3440 | { 3441 | std::exception_ptr eptr2 = error_cast(rhs).get(); 3442 | if (eptr == eptr2) return true; 3443 | 3444 | error e1 = error_from_exception(eptr); 3445 | error e2 = error_from_exception(eptr2); 3446 | return e1.domain().equivalent(e1, e2); 3447 | } 3448 | else if (rhs.domain() == error_code_domain) 3449 | { 3450 | std::error_code ec = error_code_from_exception(eptr); 3451 | return error_code_domain.equivalent(rhs, error{ec}); 3452 | } 3453 | 3454 | error e = error_from_exception(eptr); 3455 | return e.domain().equivalent(e, rhs); 3456 | } 3457 | 3458 | // ---------- DynamicExceptionCodeErrorDomain 3459 | // 3460 | inline bool dynamic_exception_code_error_domain::equivalent( 3461 | const error& lhs, 3462 | const error& rhs 3463 | ) const noexcept 3464 | { 3465 | assert(lhs.domain() == *this); 3466 | 3467 | const dynamic_exception_errc code = error_cast(lhs); 3468 | 3469 | if (rhs.domain() == *this) 3470 | { 3471 | return code == error_cast(rhs); 3472 | } 3473 | else if (rhs.domain() == error_code_domain) 3474 | { 3475 | return error_code_domain.equivalent(rhs, make_error_code(code)); 3476 | } 3477 | else if (rhs.domain() == generic_domain) 3478 | { 3479 | std::errc generic_code = dynamic_exception_code_to_generic_code(code); 3480 | return generic_domain.equivalent(rhs, generic_code); 3481 | } 3482 | 3483 | return false; 3484 | } 3485 | 3486 | inline string_ref dynamic_exception_code_error_domain::message(const error& e) const noexcept 3487 | { 3488 | assert(e.domain() == *this); 3489 | return string_ref{ 3490 | dynamic_exception_errc_str(static_cast(error_cast(e))) 3491 | }; 3492 | } 3493 | 3494 | } // end namespace stdx 3495 | 3496 | 3497 | 3498 | -------------------------------------------------------------------------------- /error.cpp: -------------------------------------------------------------------------------- 1 | #include "include/error.hpp" 2 | 3 | #if __cplusplus >= 201703L 4 | #include 5 | #include 6 | #include 7 | #endif 8 | 9 | #include 10 | 11 | namespace stdx { 12 | 13 | namespace { 14 | 15 | inline const char* dynamic_exception_errc_str(unsigned ev) noexcept 16 | { 17 | constexpr const char* msg[] = 18 | { 19 | "Success", 20 | "std::runtime_error", 21 | "std::domain_error", 22 | "std::invalid_argument", 23 | "std::length_error", 24 | "std::out_of_range", 25 | "std::logic_error", 26 | "std::range_error", 27 | "std::overflow_error", 28 | "std::underflow_error", 29 | "std::bad_alloc", 30 | "std::bad_array_new_length", 31 | "std::bad_optional_access", 32 | "std::bad_typeid", 33 | "std::bad_any_cast", 34 | "std::bad_cast", 35 | "std::bad_weak_ptr", 36 | "std::bad_function_call", 37 | "std::bad_exception", 38 | "std::bad_variant_access", 39 | "unspecified dynamic exception" 40 | }; 41 | 42 | assert(ev < (sizeof(msg) / sizeof(const char*))); 43 | return msg[ev]; 44 | } 45 | 46 | class dynamic_exception_error_category : public std::error_category 47 | { 48 | public: 49 | 50 | const char* name() const noexcept override 51 | { 52 | return "dynamic_exception"; 53 | } 54 | 55 | std::string message(int code) const override 56 | { 57 | return dynamic_exception_errc_str(code); 58 | } 59 | 60 | bool equivalent(int code, const std::error_condition& cond) const noexcept override 61 | { 62 | switch (static_cast(code)) 63 | { 64 | case dynamic_exception_errc::domain_error: 65 | return (cond == std::errc::argument_out_of_domain); 66 | case dynamic_exception_errc::invalid_argument: 67 | return (cond == std::errc::invalid_argument); 68 | case dynamic_exception_errc::length_error: 69 | return (cond == std::errc::value_too_large); 70 | case dynamic_exception_errc::out_of_range: 71 | case dynamic_exception_errc::range_error: 72 | case dynamic_exception_errc::underflow_error: 73 | return (cond == std::errc::result_out_of_range); 74 | case dynamic_exception_errc::overflow_error: 75 | return (cond == std::errc::value_too_large); 76 | case dynamic_exception_errc::bad_alloc: 77 | case dynamic_exception_errc::bad_array_new_length: 78 | return (cond == std::errc::not_enough_memory); 79 | default:; 80 | } 81 | return false; 82 | } 83 | }; 84 | 85 | const dynamic_exception_error_category dynamic_exception_error_category_instance; 86 | 87 | } // end anonymous namespace 88 | 89 | const std::error_category& dynamic_exception_category() noexcept 90 | { 91 | return dynamic_exception_error_category_instance; 92 | } 93 | 94 | std::error_code error_code_from_exception( 95 | std::exception_ptr eptr, 96 | std::error_code not_matched 97 | ) noexcept 98 | { 99 | if (!eptr) return make_error_code(dynamic_exception_errc::bad_exception); 100 | 101 | try { std::rethrow_exception(eptr); } 102 | catch (const std::domain_error&) 103 | { 104 | return make_error_code(dynamic_exception_errc::domain_error); 105 | } 106 | catch (const std::invalid_argument&) 107 | { 108 | return make_error_code(dynamic_exception_errc::invalid_argument); 109 | } 110 | catch (const std::length_error&) 111 | { 112 | return make_error_code(dynamic_exception_errc::length_error); 113 | } 114 | catch (const std::out_of_range&) 115 | { 116 | return make_error_code(dynamic_exception_errc::out_of_range); 117 | } 118 | catch (const std::logic_error&) 119 | { 120 | return make_error_code(dynamic_exception_errc::logic_error); 121 | } 122 | catch (const std::range_error&) 123 | { 124 | return make_error_code(dynamic_exception_errc::range_error); 125 | } 126 | catch (const std::overflow_error&) { 127 | return make_error_code(dynamic_exception_errc::overflow_error); 128 | } 129 | catch (const std::underflow_error&) { 130 | return make_error_code(dynamic_exception_errc::underflow_error); 131 | } 132 | catch (const std::system_error& e) 133 | { 134 | return e.code(); 135 | } 136 | catch (const std::runtime_error&) 137 | { 138 | return make_error_code(dynamic_exception_errc::runtime_error); 139 | } 140 | catch (const std::bad_array_new_length&) 141 | { 142 | return make_error_code(dynamic_exception_errc::bad_array_new_length); 143 | } 144 | catch (const std::bad_alloc&) 145 | { 146 | return make_error_code(dynamic_exception_errc::bad_alloc); 147 | } 148 | catch (const std::bad_typeid&) 149 | { 150 | return make_error_code(dynamic_exception_errc::bad_typeid); 151 | } 152 | #if __cplusplus >= 201703L 153 | catch (const std::bad_optional_access&) 154 | { 155 | return make_error_code(dynamic_exception_errc::bad_optional_access); 156 | } 157 | catch (const std::bad_any_cast&) 158 | { 159 | return make_error_code(dynamic_exception_errc::bad_any_cast); 160 | } 161 | catch (const std::bad_variant_access&) 162 | { 163 | return make_error_code(dynamic_exception_errc::bad_variant_access); 164 | } 165 | #endif 166 | catch (const std::bad_cast&) 167 | { 168 | return make_error_code(dynamic_exception_errc::bad_cast); 169 | } 170 | catch (const std::bad_weak_ptr&) 171 | { 172 | return make_error_code(dynamic_exception_errc::bad_weak_ptr); 173 | } 174 | catch (const std::bad_function_call&) 175 | { 176 | return make_error_code(dynamic_exception_errc::bad_function_call); 177 | } 178 | catch (const std::bad_exception&) 179 | { 180 | return make_error_code(dynamic_exception_errc::bad_exception); 181 | } 182 | catch (...) 183 | { } 184 | 185 | return not_matched; 186 | } 187 | 188 | error error_from_exception(std::exception_ptr eptr) noexcept 189 | { 190 | if (!eptr) return make_error(dynamic_exception_errc::bad_exception); 191 | 192 | try 193 | { 194 | std::rethrow_exception(eptr); 195 | } 196 | catch (const std::domain_error&) 197 | { 198 | return make_error(dynamic_exception_errc::domain_error); 199 | } 200 | catch (const std::invalid_argument&) 201 | { 202 | return make_error(dynamic_exception_errc::invalid_argument); 203 | } 204 | catch (const std::length_error&) 205 | { 206 | return make_error(dynamic_exception_errc::length_error); 207 | } 208 | catch (const std::out_of_range&) 209 | { 210 | return make_error(dynamic_exception_errc::out_of_range); 211 | } 212 | catch (const std::logic_error&) 213 | { 214 | return make_error(dynamic_exception_errc::logic_error); 215 | } 216 | catch (const std::range_error&) 217 | { 218 | return make_error(dynamic_exception_errc::range_error); 219 | } 220 | catch (const std::overflow_error&) 221 | { 222 | return make_error(dynamic_exception_errc::overflow_error); 223 | } 224 | catch (const std::underflow_error&) 225 | { 226 | return make_error(dynamic_exception_errc::underflow_error); 227 | } 228 | catch (const std::system_error& e) 229 | { 230 | return error{e.code()}; 231 | } 232 | catch (const std::runtime_error&) 233 | { 234 | return make_error(dynamic_exception_errc::runtime_error); 235 | } 236 | catch (const std::bad_array_new_length&) 237 | { 238 | return make_error(dynamic_exception_errc::bad_array_new_length); 239 | } 240 | catch (const std::bad_alloc&) 241 | { 242 | return make_error(dynamic_exception_errc::bad_alloc); 243 | } 244 | catch (const std::bad_typeid&) 245 | { 246 | return make_error(dynamic_exception_errc::bad_typeid); 247 | } 248 | #if __cplusplus >= 201703L 249 | catch (const std::bad_optional_access&) 250 | { 251 | return make_error(dynamic_exception_errc::bad_optional_access); 252 | } 253 | catch (const std::bad_any_cast&) 254 | { 255 | return make_error(dynamic_exception_errc::bad_any_cast); 256 | } 257 | catch (const std::bad_variant_access&) 258 | { 259 | return make_error(dynamic_exception_errc::bad_variant_access); 260 | } 261 | #endif 262 | catch (const std::bad_cast&) 263 | { 264 | return make_error(dynamic_exception_errc::bad_cast); 265 | } 266 | catch (const std::bad_weak_ptr&) 267 | { 268 | return make_error(dynamic_exception_errc::bad_weak_ptr); 269 | } 270 | catch (const std::bad_function_call&) 271 | { 272 | return make_error(dynamic_exception_errc::bad_function_call); 273 | } 274 | catch (const std::bad_exception&) 275 | { 276 | return make_error(dynamic_exception_errc::bad_exception); 277 | } 278 | catch (...) 279 | { } 280 | 281 | return make_error(dynamic_exception_errc::unspecified_exception); 282 | } 283 | 284 | // ---------- ErrorDomain (abstract base class) 285 | // 286 | void error_domain::throw_exception(const error& e) const 287 | { 288 | throw thrown_dynamic_exception{e}; 289 | } 290 | 291 | // ---------- GenericErrorDomain 292 | // 293 | bool generic_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 294 | { 295 | assert(lhs.domain() == *this); 296 | if (lhs.domain() == rhs.domain()) 297 | { 298 | return error_cast(lhs) == error_cast(rhs); 299 | } 300 | 301 | return false; 302 | } 303 | 304 | namespace { 305 | 306 | string_ref generic_error_code_message(std::errc code) noexcept 307 | { 308 | switch (code) 309 | { 310 | case std::errc::address_family_not_supported: 311 | return "Address family not supported by protocol"; 312 | case std::errc::address_in_use: 313 | return "Address already in use"; 314 | case std::errc::address_not_available: 315 | return "Cannot assign requested address"; 316 | case std::errc::already_connected: 317 | return "Transport endpoint is already connected"; 318 | case std::errc::argument_list_too_long: 319 | return "Argument list too long"; 320 | case std::errc::argument_out_of_domain: 321 | return "Numerical argument out of domain"; 322 | case std::errc::bad_address: 323 | return "Bad address"; 324 | case std::errc::bad_file_descriptor: 325 | return "Bad file descriptor"; 326 | case std::errc::bad_message: 327 | return "Bad message"; 328 | case std::errc::broken_pipe: 329 | return "Broken pipe"; 330 | case std::errc::connection_aborted: 331 | return "Software caused connection abort"; 332 | case std::errc::connection_already_in_progress: 333 | return "Operation already in progress"; 334 | case std::errc::connection_refused: 335 | return "Connection refused"; 336 | case std::errc::connection_reset: 337 | return "Connection reset by peer"; 338 | case std::errc::cross_device_link: 339 | return "Invalid cross-device link"; 340 | case std::errc::destination_address_required: 341 | return "Destination address required"; 342 | case std::errc::device_or_resource_busy: 343 | return "Device or resource busy"; 344 | case std::errc::directory_not_empty: 345 | return "Directory not empty"; 346 | case std::errc::executable_format_error: 347 | return "Exec format error"; 348 | case std::errc::file_exists: 349 | return "File exists"; 350 | case std::errc::file_too_large: 351 | return "File too large"; 352 | case std::errc::filename_too_long: 353 | return "File name too long"; 354 | case std::errc::function_not_supported: 355 | return "Function not implemented"; 356 | case std::errc::host_unreachable: 357 | return "No route to host"; 358 | case std::errc::identifier_removed: 359 | return "Identifier removed"; 360 | case std::errc::illegal_byte_sequence: 361 | return "Invalid or incomplete multibyte or wide character"; 362 | case std::errc::inappropriate_io_control_operation: 363 | return "Inappropriate ioctl for device"; 364 | case std::errc::interrupted: 365 | return "Interrupted system call"; 366 | case std::errc::invalid_argument: 367 | return "Invalid argument"; 368 | case std::errc::invalid_seek: 369 | return "Illegal seek"; 370 | case std::errc::io_error: 371 | return "Input/output error"; 372 | case std::errc::is_a_directory: 373 | return "Is a directory"; 374 | case std::errc::message_size: 375 | return "Message too long"; 376 | case std::errc::network_down: 377 | return "Network is down"; 378 | case std::errc::network_reset: 379 | return "Network dropped connection on reset"; 380 | case std::errc::network_unreachable: 381 | return "Network is unreachable"; 382 | case std::errc::no_buffer_space: 383 | return "No buffer space available"; 384 | case std::errc::no_child_process: 385 | return "No child processes"; 386 | case std::errc::no_link: 387 | return "Link has been severed"; 388 | case std::errc::no_lock_available: 389 | return "No locks available"; 390 | case std::errc::no_message: 391 | return "No message of desired type"; 392 | case std::errc::no_protocol_option: 393 | return "Protocol not available"; 394 | case std::errc::no_space_on_device: 395 | return "No space left on device"; 396 | case std::errc::no_stream_resources: 397 | return "Out of streams resources"; 398 | case std::errc::no_such_device_or_address: 399 | return "No such device or address"; 400 | case std::errc::no_such_device: 401 | return "No such device"; 402 | case std::errc::no_such_file_or_directory: 403 | return "No such file or directory"; 404 | case std::errc::no_such_process: 405 | return "No such process"; 406 | case std::errc::not_a_directory: 407 | return "Not a directory"; 408 | case std::errc::not_a_socket: 409 | return "Socket operation on non-socket"; 410 | case std::errc::not_a_stream: 411 | return "Device not a stream"; 412 | case std::errc::not_connected: 413 | return "Transport endpoint is not connected"; 414 | case std::errc::not_enough_memory: 415 | return "Cannot allocate memory"; 416 | #if ENOTSUP != EOPNOTSUPP 417 | case std::errc::not_supported: 418 | return "Operation not supported"; 419 | #endif 420 | case std::errc::operation_canceled: 421 | return "Operation canceled"; 422 | case std::errc::operation_in_progress: 423 | return "Operation now in progress"; 424 | case std::errc::operation_not_permitted: 425 | return "Operation not permitted"; 426 | case std::errc::operation_not_supported: 427 | return "Operation not supported"; 428 | #if EAGAIN != EWOULDBLOCK 429 | case std::errc::operation_would_block: 430 | return "Resource temporarily unavailable"; 431 | #endif 432 | case std::errc::owner_dead: 433 | return "Owner died"; 434 | case std::errc::permission_denied: 435 | return "Permission denied"; 436 | case std::errc::protocol_error: 437 | return "Protocol error"; 438 | case std::errc::protocol_not_supported: 439 | return "Protocol not supported"; 440 | case std::errc::read_only_file_system: 441 | return "Read-only file system"; 442 | case std::errc::resource_deadlock_would_occur: 443 | return "Resource deadlock avoided"; 444 | case std::errc::resource_unavailable_try_again: 445 | return "Resource temporarily unavailable"; 446 | case std::errc::result_out_of_range: 447 | return "Numerical result out of range"; 448 | case std::errc::state_not_recoverable: 449 | return "State not recoverable"; 450 | case std::errc::stream_timeout: 451 | return "Timer expired"; 452 | case std::errc::text_file_busy: 453 | return "Text file busy"; 454 | case std::errc::timed_out: 455 | return "Connection timed out"; 456 | case std::errc::too_many_files_open_in_system: 457 | return "Too many open files in system"; 458 | case std::errc::too_many_files_open: 459 | return "Too many open files"; 460 | case std::errc::too_many_links: 461 | return "Too many links"; 462 | case std::errc::too_many_symbolic_link_levels: 463 | return "Too many levels of symbolic links"; 464 | case std::errc::value_too_large: 465 | return "Value too large for defined data type"; 466 | case std::errc::wrong_protocol_type: 467 | return "Protocol wrong type for socket"; 468 | default: 469 | return "Unspecified error"; 470 | } 471 | } 472 | 473 | } // end anonymous namespace 474 | 475 | string_ref generic_error_domain::message(const error& e) const noexcept 476 | { 477 | assert(e.domain() == *this); 478 | return generic_error_code_message(error_cast(e)); 479 | } 480 | 481 | // ---------- ErrorCodeErrorDomain 482 | // 483 | string_ref error_code_error_domain::message(const error& e) const noexcept 484 | { 485 | assert(e.domain() == *this); 486 | 487 | auto ptr = error_cast(e); 488 | if (ptr) 489 | { 490 | std::string msg = ptr->code.message(); 491 | return shared_string_ref{msg.c_str(), msg.c_str() + msg.size()}; 492 | } 493 | 494 | return string_ref{"Bad error code"}; 495 | } 496 | 497 | void error_code_error_domain::throw_exception(const error& e) const 498 | { 499 | assert(e.domain() == *this); 500 | 501 | std::error_code code; 502 | auto ptr = error_cast(e); 503 | if (ptr) code = ptr->code; 504 | throw std::system_error{code}; 505 | } 506 | 507 | bool error_code_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 508 | { 509 | assert(lhs.domain() == *this); 510 | 511 | if (lhs.domain() == rhs.domain()) 512 | { 513 | auto ptr1 = error_cast(lhs); 514 | auto ptr2 = error_cast(rhs); 515 | if (ptr1 && ptr2) return ptr1->code == ptr2->code.default_error_condition(); 516 | return false; 517 | } 518 | 519 | if (rhs.domain() == generic_domain) 520 | { 521 | auto ptr1 = error_cast(lhs); 522 | if (ptr1) return ptr1->code == error_cast(rhs); 523 | } 524 | 525 | return false; 526 | } 527 | 528 | stdx::error error_traits::to_error(std::error_code ec) noexcept 529 | { 530 | using internal_value_type = error_code_error_domain::internal_value_type; 531 | 532 | if (ec.category() == std::generic_category()) 533 | { 534 | return error{ 535 | error_value{static_cast(ec.default_error_condition().value())}, 536 | generic_domain 537 | }; 538 | } 539 | 540 | return error{ 541 | error_value{internal_value_type{new detail::error_code_wrapper{ec}}}, 542 | error_code_domain 543 | }; 544 | } 545 | 546 | // ---------- DynamicExceptionErrorDomain 547 | // 548 | string_ref dynamic_exception_error_domain::message(const error& e) const noexcept 549 | { 550 | assert(e.domain() == *this); 551 | 552 | std::exception_ptr eptr = error_cast(e).get(); 553 | 554 | try 555 | { 556 | std::rethrow_exception(eptr); 557 | } 558 | catch (const std::exception& ex) 559 | { 560 | return shared_string_ref{ex.what()}; 561 | } 562 | catch (...) {} 563 | 564 | return string_ref{"Unknown dynamic exception"}; 565 | } 566 | 567 | namespace { 568 | 569 | std::errc dynamic_exception_code_to_generic_code(dynamic_exception_errc code) noexcept 570 | { 571 | switch (code) 572 | { 573 | case dynamic_exception_errc::domain_error: 574 | return std::errc::argument_out_of_domain; 575 | case dynamic_exception_errc::invalid_argument: 576 | return std::errc::invalid_argument; 577 | case dynamic_exception_errc::length_error: 578 | return std::errc::value_too_large; 579 | case dynamic_exception_errc::out_of_range: 580 | case dynamic_exception_errc::range_error: 581 | case dynamic_exception_errc::underflow_error: 582 | return std::errc::result_out_of_range; 583 | case dynamic_exception_errc::overflow_error: 584 | return std::errc::value_too_large; 585 | case dynamic_exception_errc::bad_alloc: 586 | case dynamic_exception_errc::bad_array_new_length: 587 | return std::errc::not_enough_memory; 588 | default:; 589 | } 590 | return std::errc{}; 591 | } 592 | 593 | } // end anonymous namespace 594 | 595 | bool dynamic_exception_error_domain::equivalent(const error& lhs, const error& rhs) const noexcept 596 | { 597 | assert(lhs.domain() == *this); 598 | 599 | std::exception_ptr eptr = error_cast(lhs).get(); 600 | 601 | if (rhs.domain() == *this) 602 | { 603 | std::exception_ptr eptr2 = error_cast(rhs).get(); 604 | if (eptr == eptr2) return true; 605 | 606 | error e1 = error_from_exception(eptr); 607 | error e2 = error_from_exception(eptr2); 608 | return e1.domain().equivalent(e1, e2); 609 | } 610 | else if (rhs.domain() == error_code_domain) 611 | { 612 | std::error_code ec = error_code_from_exception(eptr); 613 | return error_code_domain.equivalent(rhs, error{ec}); 614 | } 615 | 616 | error e = error_from_exception(eptr); 617 | return e.domain().equivalent(e, rhs); 618 | } 619 | 620 | // ---------- DynamicExceptionCodeErrorDomain 621 | // 622 | bool dynamic_exception_code_error_domain::equivalent( 623 | const error& lhs, 624 | const error& rhs 625 | ) const noexcept 626 | { 627 | assert(lhs.domain() == *this); 628 | 629 | const dynamic_exception_errc code = error_cast(lhs); 630 | 631 | if (rhs.domain() == *this) 632 | { 633 | return code == error_cast(rhs); 634 | } 635 | else if (rhs.domain() == error_code_domain) 636 | { 637 | return error_code_domain.equivalent(rhs, make_error_code(code)); 638 | } 639 | else if (rhs.domain() == generic_domain) 640 | { 641 | std::errc generic_code = dynamic_exception_code_to_generic_code(code); 642 | return generic_domain.equivalent(rhs, generic_code); 643 | } 644 | 645 | return false; 646 | } 647 | 648 | string_ref dynamic_exception_code_error_domain::message(const error& e) const noexcept 649 | { 650 | assert(e.domain() == *this); 651 | return string_ref{ 652 | dynamic_exception_errc_str(static_cast(error_cast(e))) 653 | }; 654 | } 655 | 656 | } // end namespace stdx 657 | 658 | 659 | -------------------------------------------------------------------------------- /include/bit_cast.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_BIT_CAST_HPP 2 | #define STDX_BIT_CAST_HPP 3 | 4 | #include 5 | #include 6 | 7 | #include "compiler.hpp" 8 | #include "type_traits.hpp" 9 | 10 | namespace stdx { 11 | 12 | namespace detail { 13 | 14 | template 15 | using use_static_cast = bool_constant< 16 | ((std::is_integral::value || std::is_enum::value) 17 | && (std::is_integral::value || std::is_enum::value)) 18 | || (std::is_same::value && std::is_copy_constructible::value) 19 | >; 20 | 21 | template 22 | using is_integral_ptr_t = bool_constant< 23 | std::is_same::value 24 | || std::is_same::value 25 | >; 26 | 27 | template 28 | using use_reinterpret_cast = bool_constant< 29 | !std::is_same::value 30 | && (( 31 | std::is_pointer::value 32 | && std::is_pointer::value 33 | && std::is_convertible::value 34 | ) 35 | || (std::is_pointer::value && is_integral_ptr_t::value) 36 | || (std::is_pointer::value && is_integral_ptr_t::value) 37 | ) 38 | >; 39 | 40 | #if defined(STDX_GCC_COMPILER) 41 | template 42 | using use_union_type_punning = bool_constant< 43 | !use_static_cast::value 44 | && !use_reinterpret_cast::value 45 | && !std::is_array::value 46 | && !std::is_array::value 47 | >; 48 | 49 | template 50 | union bit_cast_union 51 | { 52 | From from; 53 | To to; 54 | }; 55 | #else 56 | template 57 | using use_union_type_punning = std::false_type; 58 | #endif 59 | 60 | template 61 | using can_bit_cast = bool_constant< 62 | (sizeof(To) == sizeof(From)) 63 | && is_trivially_copyable::value 64 | && is_trivially_copyable::value 65 | >; 66 | 67 | } // end namespace detail 68 | 69 | template < 70 | class To, 71 | class From, 72 | class = std::enable_if_t< 73 | detail::can_bit_cast::value 74 | && detail::use_static_cast::value 75 | > 76 | > 77 | constexpr To bit_cast(const From& from) noexcept 78 | { 79 | return static_cast(from); 80 | } 81 | 82 | template < 83 | class To, 84 | class From, 85 | class = std::enable_if_t< 86 | detail::can_bit_cast::value 87 | && detail::use_reinterpret_cast::value 88 | >, 89 | int = 0 90 | > 91 | constexpr To bit_cast(const From& from) noexcept 92 | { 93 | return reinterpret_cast(from); 94 | } 95 | 96 | #if defined(STDX_GCC_COMPILER) // GCC allows union type punning 97 | template < 98 | class To, 99 | class From, 100 | class = std::enable_if_t< 101 | detail::can_bit_cast::value 102 | && detail::use_union_type_punning::value 103 | >, 104 | class = void 105 | > 106 | constexpr To bit_cast(const From& from) noexcept 107 | { 108 | return detail::bit_cast_union{from}.to; 109 | } 110 | #elif defined(STDX_CLANG_COMPILER) 111 | #if __has_builtin(__builtin_bit_cast) 112 | template < 113 | class To, 114 | class From, 115 | class = std::enable_if_t< 116 | detail::can_bit_cast::value 117 | && !detail::use_static_cast::value 118 | && !detail::use_reinterpret_cast::value 119 | >, 120 | class = void 121 | > 122 | constexpr To bit_cast(const From& from) noexcept 123 | { 124 | return __builtin_bit_cast(To, from); 125 | } 126 | #else 127 | template < 128 | class To, 129 | class From, 130 | class = std::enable_if_t< 131 | detail::can_bit_cast::value 132 | && !detail::use_static_cast::value 133 | && !detail::use_reinterpret_cast::value 134 | >, 135 | class = void 136 | > 137 | To bit_cast(const From& from) noexcept 138 | { 139 | To to; 140 | std::memcpy(&to, &from, sizeof(To)); 141 | return to; 142 | } 143 | #endif 144 | #endif // STDX_CLANG_COMPILER 145 | 146 | template 147 | struct is_bit_castable 148 | : 149 | bool_constant< 150 | (sizeof(To) == sizeof(From)) 151 | && is_trivially_copyable::value 152 | && is_trivially_copyable::value 153 | > 154 | { }; 155 | 156 | } // end namespace stdx 157 | 158 | #endif 159 | 160 | 161 | -------------------------------------------------------------------------------- /include/compiler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_COMPILER_HPP 2 | #define STDX_COMPILER_HPP 3 | 4 | // Check compiler macros. Note that Clang defines __GNUC__ and other GNU macros as well, 5 | // but GNU does not define Clang macros, so we must check for Clang first. 6 | 7 | #if defined(__llvm__) || defined(__clang__) 8 | 9 | // -------- LLVM/Clang 10 | 11 | #define STDX_CLANG_COMPILER 1 12 | 13 | #if defined(__cpp_variable_templates) && (__cplusplus >= 201703L) 14 | #define STDX_VARIABLE_TEMPLATES 1 15 | #endif 16 | 17 | #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) 18 | 19 | // -------- GNU G++ 20 | 21 | #include 22 | 23 | #define STDX_GCC_COMPILER 1 24 | 25 | #if (__GNUC__ >= 5) && (__cplusplus >= 201703L) 26 | #define STDX_VARIABLE_TEMPLATES 1 27 | #endif 28 | 29 | #if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1))) 30 | #define STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE 1 31 | #endif 32 | 33 | #endif 34 | 35 | #if defined(STDX_GCC_COMPILER) 36 | #if (__GNUC__ == 7) && ((__GNUC_MINOR__ >= 1) && (__GNUC_MINOR__ <= 3)) 37 | #define STDX_GCC7_WORKAROUND_CONSTEXPR 38 | #else 39 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr 40 | #endif 41 | #else 42 | #define STDX_GCC7_WORKAROUND_CONSTEXPR constexpr 43 | #endif 44 | 45 | // Add a legacy constexpr macro for cases where GCC < 5 incorrectly applies the const 46 | // qualifier to constexpr member functions or does not support relaxed constexpr functions 47 | // 48 | #if defined(STDX_GCC_COMPILER) && (__GNUC__ < 5) 49 | #define STDX_LEGACY_CONSTEXPR 50 | #else 51 | #define STDX_LEGACY_CONSTEXPR constexpr 52 | #endif 53 | 54 | #if defined(_MSC_VER) && (_MSC_VER >= 1910) 55 | #define STDX_MSVC_EMPTY_BASE_CLASSES __declspec(empty_bases) 56 | #else 57 | #define STDX_MSVC_EMPTY_BASE_CLASSES 58 | #endif 59 | 60 | #if defined(__cpp_impl_trivially_relocatable) 61 | #define STDX_TRIVIALLY_RELOCATABLE [[trivially_relocatable]] 62 | #else 63 | #define STDX_TRIVIALLY_RELOCATABLE 64 | #endif 65 | 66 | #endif // STDX_COMPILER_HPP 67 | 68 | -------------------------------------------------------------------------------- /include/error.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_ERROR_HPP 2 | #define STDX_ERROR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "compiler.hpp" 11 | #include "bit_cast.hpp" 12 | #include "launder.hpp" 13 | #include "string_ref.hpp" 14 | #include "intrusive_ptr.hpp" 15 | 16 | namespace stdx { 17 | 18 | class error; 19 | 20 | namespace detail { 21 | 22 | template 23 | struct error_ctor_args; 24 | 25 | } // end namespace detail 26 | 27 | } // end namespace stdx 28 | 29 | namespace stdx_adl { 30 | 31 | namespace detail { 32 | 33 | template 34 | struct can_use_adl_to_call_make_error : std::false_type 35 | { }; 36 | 37 | inline void make_error() noexcept { } 38 | 39 | template 40 | struct can_use_adl_to_call_make_error< 41 | stdx::detail::error_ctor_args, 42 | std::enable_if_t< 43 | std::is_same< 44 | decltype(make_error(std::declval()...)), 45 | stdx::error 46 | >::value 47 | > 48 | > 49 | : std::true_type 50 | { }; 51 | 52 | template 53 | constexpr auto construct_error_from_adl(Args&&... args) noexcept( 54 | noexcept(make_error(std::forward(args)...)) 55 | ) 56 | { 57 | return make_error(std::forward(args)...); 58 | } 59 | } 60 | 61 | } // end namespace stdx_adl 62 | 63 | namespace stdx { 64 | 65 | enum class dynamic_exception_errc 66 | { 67 | runtime_error = 1, 68 | domain_error, 69 | invalid_argument, 70 | length_error, 71 | out_of_range, 72 | logic_error, 73 | range_error, 74 | overflow_error, 75 | underflow_error, 76 | bad_alloc, 77 | bad_array_new_length, 78 | bad_optional_access, 79 | bad_typeid, 80 | bad_any_cast, 81 | bad_cast, 82 | bad_weak_ptr, 83 | bad_function_call, 84 | bad_exception, 85 | bad_variant_access, 86 | unspecified_exception 87 | }; 88 | 89 | const std::error_category& dynamic_exception_category() noexcept; 90 | 91 | inline std::error_code make_error_code(dynamic_exception_errc code) noexcept 92 | { 93 | return std::error_code(static_cast(code), dynamic_exception_category()); 94 | } 95 | 96 | std::error_code error_code_from_exception( 97 | std::exception_ptr eptr = std::current_exception(), 98 | std::error_code not_matched = make_error_code(dynamic_exception_errc::unspecified_exception) 99 | ) noexcept; 100 | 101 | // -------------------- error_traits 102 | // 103 | template 104 | struct error_traits 105 | { }; 106 | 107 | namespace detail { 108 | 109 | template 110 | struct is_convertible_from_exception_using_traits : std::false_type 111 | { }; 112 | 113 | template 114 | struct is_convertible_from_exception_using_traits< 115 | E, 116 | std::enable_if_t< 117 | std::is_convertible< 118 | decltype(error_traits::from_exception(std::declval())), 119 | E 120 | >::value 121 | > 122 | > 123 | : std::true_type 124 | { }; 125 | 126 | template 127 | struct is_convertible_to_exception_using_traits : std::false_type 128 | { }; 129 | 130 | template 131 | struct is_convertible_to_exception_using_traits< 132 | E, 133 | std::enable_if_t< 134 | std::is_convertible< 135 | decltype(error_traits::to_exception(std::declval())), 136 | std::exception_ptr 137 | >::value 138 | > 139 | > 140 | : std::true_type 141 | { }; 142 | 143 | template 144 | E from_exception_impl(std::exception_ptr e, std::is_convertible) noexcept 145 | { 146 | return e; 147 | } 148 | 149 | template 150 | E from_exception_impl( 151 | std::exception_ptr e, 152 | is_convertible_from_exception_using_traits 153 | ) noexcept 154 | { 155 | return error_traits::from_exception(std::move(e)); 156 | } 157 | 158 | void from_exception_impl(std::exception_ptr, sentinel_type) = delete; 159 | 160 | template 161 | std::exception_ptr to_exception_impl(E&& e, std::is_convertible) noexcept 162 | { 163 | return std::forward(e); 164 | } 165 | 166 | template 167 | std::exception_ptr to_exception_impl( 168 | E&& e, 169 | is_convertible_to_exception_using_traits 170 | ) noexcept 171 | { 172 | return error_traits::to_exception(std::forward(e)); 173 | } 174 | 175 | template 176 | void to_exception_impl(const E&, sentinel_type) = delete; 177 | 178 | } // end namespace detail 179 | 180 | template 181 | E from_exception(std::exception_ptr e) noexcept 182 | { 183 | return detail::from_exception_impl( 184 | std::move(e), 185 | disjunction< 186 | std::is_convertible, 187 | detail::is_convertible_from_exception_using_traits, 188 | sentinel_type 189 | >{} 190 | ); 191 | } 192 | 193 | template 194 | std::exception_ptr to_exception(E&& e) noexcept 195 | { 196 | return detail::to_exception_impl( 197 | std::forward(e), 198 | disjunction< 199 | std::is_convertible, std::exception_ptr>, 200 | detail::is_convertible_to_exception_using_traits>, 201 | sentinel_type 202 | >{} 203 | ); 204 | } 205 | 206 | struct error_domain_id 207 | { 208 | constexpr error_domain_id(std::uint64_t l, std::uint64_t h) noexcept 209 | : lo(l), hi(h) 210 | { } 211 | 212 | private: 213 | 214 | friend constexpr bool operator == (const error_domain_id&, const error_domain_id&) noexcept; 215 | 216 | std::uint64_t lo; 217 | std::uint64_t hi; 218 | }; 219 | 220 | constexpr bool operator == (const error_domain_id& lhs, const error_domain_id& rhs) noexcept 221 | { 222 | return (lhs.lo == rhs.lo) && (lhs.hi == rhs.hi); 223 | } 224 | 225 | constexpr bool operator != (const error_domain_id& lhs, const error_domain_id& rhs) noexcept 226 | { 227 | return !(lhs == rhs); 228 | } 229 | 230 | template 231 | struct error_value; 232 | 233 | namespace detail { 234 | 235 | template 236 | struct is_error_value : std::false_type 237 | { }; 238 | 239 | template 240 | struct is_error_value> : std::true_type 241 | { }; 242 | 243 | } // end namespace detail 244 | 245 | struct error_resource_management 246 | { 247 | using copy_constructor = error_value(*)(const error&); 248 | using move_constructor = error_value(*)(error&&); 249 | using destructor = void(*)(error&); 250 | 251 | constexpr error_resource_management() noexcept 252 | : copy{nullptr}, move{nullptr}, destroy{nullptr} 253 | { } 254 | 255 | constexpr error_resource_management( 256 | copy_constructor cctor, 257 | move_constructor mctor, 258 | destructor dtor 259 | ) noexcept 260 | : copy(cctor), move(mctor), destroy(dtor) 261 | { } 262 | 263 | copy_constructor copy; 264 | move_constructor move; 265 | destructor destroy; 266 | }; 267 | 268 | class error_domain 269 | { 270 | public: 271 | 272 | virtual string_ref name() const noexcept = 0; 273 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept = 0; 274 | virtual string_ref message(const error&) const noexcept = 0; 275 | 276 | virtual void throw_exception(const error& e) const; 277 | 278 | friend class error; 279 | friend constexpr bool operator == (const error_domain&, const error_domain&) noexcept; 280 | friend constexpr bool operator != (const error_domain&, const error_domain&) noexcept; 281 | 282 | constexpr error_domain_id id() const noexcept 283 | { 284 | return m_id; 285 | } 286 | 287 | protected: 288 | 289 | constexpr explicit error_domain(error_domain_id id) noexcept 290 | : 291 | m_id{id}, 292 | m_resource_management{} 293 | { } 294 | 295 | constexpr error_domain(error_domain_id id, error_resource_management erm) noexcept 296 | : 297 | m_id{id}, 298 | m_resource_management{erm} 299 | { } 300 | 301 | error_domain(const error_domain &) = default; 302 | error_domain(error_domain &&) = default; 303 | error_domain &operator = (const error_domain &) = default; 304 | error_domain &operator = (error_domain&&) = default; 305 | ~error_domain() = default; 306 | 307 | template < 308 | class E = error, 309 | class = std::enable_if_t< 310 | std::is_convertible::value 311 | > 312 | > 313 | constexpr dependent_type_t> copy(const E& e) const 314 | { 315 | return m_resource_management.copy ? m_resource_management.copy(e) : error_value<>{e.m_value}; 316 | } 317 | 318 | template < 319 | class E = error, 320 | class = std::enable_if_t< 321 | std::is_rvalue_reference::value 322 | && std::is_convertible::value 323 | > 324 | > 325 | constexpr dependent_type_t, error_value<>> move(E&& e) const 326 | { 327 | return m_resource_management.move ? 328 | m_resource_management.move(static_cast(e)) : error_value<>{e.m_value}; 329 | } 330 | 331 | void destroy(error& e) const noexcept 332 | { 333 | if (m_resource_management.destroy) m_resource_management.destroy(e); 334 | } 335 | 336 | private: 337 | 338 | error_domain_id m_id; 339 | error_resource_management m_resource_management; 340 | }; 341 | 342 | constexpr bool operator == (const error_domain& lhs, const error_domain& rhs) noexcept 343 | { 344 | return lhs.id() == rhs.id(); 345 | } 346 | 347 | constexpr bool operator != (const error_domain& lhs, const error_domain& rhs) noexcept 348 | { 349 | return lhs.id() != rhs.id(); 350 | } 351 | 352 | namespace detail { 353 | 354 | template 355 | struct error_type_is_erasable 356 | : 357 | bool_constant< 358 | is_trivially_relocatable::value 359 | && (sizeof(T) <= sizeof(ErasedType)) 360 | && (alignof(T) <= alignof(ErasedType)) 361 | > 362 | { }; 363 | 364 | template 365 | struct error_type_is_erasable : std::false_type 366 | { }; 367 | 368 | template 369 | struct error_type_is_erasable : std::false_type 370 | { }; 371 | 372 | template 373 | using can_use_static_cast = bool_constant< 374 | std::is_integral::value 375 | || std::is_enum::value 376 | >; 377 | 378 | struct erased_error 379 | { 380 | using integral_type = std::intptr_t; 381 | using storage_type = std::aligned_storage_t; 382 | 383 | constexpr erased_error() noexcept : code{} 384 | { } 385 | 386 | template < 387 | class T, 388 | class = std::enable_if_t< 389 | error_type_is_erasable::value 390 | && can_use_static_cast::value 391 | > 392 | > 393 | constexpr erased_error(T value) noexcept 394 | : code{static_cast(value)} 395 | { } 396 | 397 | template < 398 | class T, 399 | class = std::enable_if_t< 400 | error_type_is_erasable::value 401 | && !can_use_static_cast::value 402 | && is_bit_castable::value 403 | >, 404 | class = void 405 | > 406 | constexpr erased_error(T value) noexcept 407 | : code{bit_cast(value)} 408 | { } 409 | 410 | template < 411 | class T, 412 | class = std::enable_if_t< 413 | error_type_is_erasable::value 414 | && !can_use_static_cast::value 415 | && !is_bit_castable::value 416 | >, 417 | int = 0 418 | > 419 | erased_error(T value) noexcept(std::is_nothrow_move_constructible::value) 420 | { 421 | new (&storage) T(std::move(value)); 422 | } 423 | 424 | union 425 | { 426 | integral_type code; 427 | storage_type storage; 428 | }; 429 | }; 430 | 431 | template < 432 | class T, 433 | class = std::enable_if_t< 434 | error_type_is_erasable::value 435 | && can_use_static_cast::value 436 | > 437 | > 438 | constexpr T error_cast_impl(erased_error e) noexcept 439 | { 440 | return static_cast(e.code); 441 | } 442 | 443 | template < 444 | class T, 445 | class = std::enable_if_t< 446 | error_type_is_erasable::value 447 | && !can_use_static_cast::value 448 | && is_bit_castable::value 449 | >, 450 | class = void 451 | > 452 | constexpr T error_cast_impl(erased_error e) noexcept 453 | { 454 | return bit_cast(e.code); 455 | } 456 | 457 | template < 458 | class T, 459 | class = std::enable_if_t< 460 | error_type_is_erasable::value 461 | && !can_use_static_cast::value 462 | && !is_bit_castable::value 463 | > 464 | > 465 | constexpr T error_cast_impl(erased_error&& e) noexcept( 466 | std::is_nothrow_move_constructible::value 467 | ) 468 | { 469 | return std::move(*stdx::launder(reinterpret_cast(&e.storage))); 470 | } 471 | 472 | template < 473 | class T, 474 | class = std::enable_if_t< 475 | error_type_is_erasable::value 476 | && !can_use_static_cast::value 477 | && !is_bit_castable::value 478 | > 479 | > 480 | constexpr T error_cast_impl(const erased_error& e) noexcept( 481 | std::is_nothrow_copy_constructible::value 482 | ) 483 | { 484 | return *stdx::launder(reinterpret_cast(&e.storage)); 485 | } 486 | 487 | } // end namespace detail 488 | 489 | template 490 | struct error_value 491 | { 492 | using value_type = T; 493 | 494 | constexpr error_value(const T& v) noexcept( 495 | std::is_nothrow_copy_constructible::value 496 | ) 497 | : m_value(v) 498 | { } 499 | 500 | constexpr error_value(T&& v) noexcept( 501 | std::is_nothrow_move_constructible::value 502 | ) 503 | : m_value(std::move(v)) 504 | { } 505 | 506 | constexpr const T& value() const & noexcept 507 | { 508 | return m_value; 509 | } 510 | 511 | STDX_LEGACY_CONSTEXPR T& value() & noexcept 512 | { 513 | return m_value; 514 | } 515 | 516 | constexpr const T&& value() const && noexcept 517 | { 518 | return static_cast(m_value); 519 | } 520 | 521 | STDX_LEGACY_CONSTEXPR T&& value() && noexcept 522 | { 523 | return static_cast(m_value); 524 | } 525 | 526 | T m_value; 527 | }; 528 | 529 | template <> 530 | struct error_value 531 | { 532 | template < 533 | class T, 534 | class = std::enable_if_t< 535 | !std::is_same, error_value>::value 536 | && !detail::is_error_value>::value 537 | && std::is_constructible::value 538 | > 539 | > 540 | constexpr error_value(T&& v) noexcept( 541 | std::is_nothrow_constructible::value 542 | ) 543 | : m_value(std::forward(v)) 544 | { } 545 | 546 | template < 547 | class T, 548 | class = std::enable_if_t< 549 | std::is_constructible::value 550 | > 551 | > 552 | constexpr error_value(const error_value& v) noexcept( 553 | std::is_nothrow_constructible::value 554 | ) 555 | : error_value(v.value()) 556 | { } 557 | 558 | template < 559 | class T, 560 | class = std::enable_if_t< 561 | std::is_constructible::value 562 | > 563 | > 564 | constexpr error_value(error_value&& v) noexcept( 565 | std::is_nothrow_constructible::value 566 | ) 567 | : error_value(std::move(v.value())) 568 | { } 569 | 570 | friend class error; 571 | 572 | private: 573 | 574 | detail::erased_error m_value; 575 | }; 576 | 577 | class error; 578 | 579 | namespace detail { 580 | 581 | struct error_copy_construct_t {}; 582 | struct error_move_construct_t {}; 583 | 584 | template 585 | struct error_ctor_args {}; 586 | 587 | template 588 | struct can_construct_error_from_adl 589 | { }; 590 | 591 | template 592 | struct can_construct_error_from_adl, error_ctor_args> 593 | : stdx_adl::detail::can_use_adl_to_call_make_error> 594 | { }; 595 | 596 | template <> 597 | struct can_construct_error_from_adl, error_ctor_args<>> : std::false_type 598 | { }; 599 | 600 | template 601 | struct can_construct_error_from_adl, error_ctor_args> 602 | : std::false_type 603 | { }; 604 | 605 | template 606 | struct can_construct_error_from_adl< 607 | error_ctor_args, 608 | error_ctor_args, ErrorDomain> 609 | > 610 | : std::false_type 611 | { }; 612 | 613 | template 614 | struct can_construct_error_from_adl< 615 | error_ctor_args, 616 | error_ctor_args, ErrorDomain> 617 | > 618 | : std::false_type 619 | { }; 620 | 621 | template 622 | struct can_construct_error_from_adl< 623 | error_ctor_args, 624 | error_ctor_args, ErrorDomain> 625 | > 626 | : std::false_type 627 | { }; 628 | 629 | template 630 | struct is_convertible_to_error_using_traits : std::false_type 631 | { }; 632 | 633 | template <> 634 | struct is_convertible_to_error_using_traits> 635 | : std::false_type 636 | { }; 637 | 638 | template 639 | struct is_convertible_to_error_using_traits< 640 | error_ctor_args, 641 | std::enable_if_t< 642 | std::is_same< 643 | decltype(error_traits::to_error(std::declval())), 644 | error 645 | >::value 646 | > 647 | > 648 | : std::true_type 649 | { }; 650 | 651 | template 652 | constexpr auto construct_error_impl( 653 | is_convertible_to_error_using_traits>, 654 | Args&&... args 655 | ) noexcept(noexcept(error_traits::to_error(std::declval()...))) 656 | { 657 | return error_traits::to_error(std::forward(args)...); 658 | } 659 | 660 | template 661 | constexpr auto construct_error_impl( 662 | can_construct_error_from_adl, 663 | Args&&... args 664 | ) noexcept(noexcept(stdx_adl::detail::construct_error_from_adl(std::declval()...))) 665 | { 666 | return stdx_adl::detail::construct_error_from_adl(std::forward(args)...); 667 | } 668 | 669 | struct cannot_construct_error 670 | { 671 | static constexpr bool value = false; 672 | using type = void; 673 | }; 674 | 675 | template 676 | void construct_error_impl(cannot_construct_error, Args&&...) = delete; 677 | 678 | template 679 | using construct_error_disjunction_impl_t = disjunction< 680 | is_convertible_to_error_using_traits, 681 | can_construct_error_from_adl, 682 | cannot_construct_error 683 | >; 684 | 685 | template 686 | using construct_error_disjunction_t = construct_error_disjunction_impl_t< 687 | error_ctor_args, 688 | error_ctor_args...> 689 | >; 690 | 691 | struct error_move_access; 692 | struct error_ref_access; 693 | struct error_cref_access; 694 | 695 | } // end namespace detail 696 | 697 | // Generic domain for std::errc codes 698 | // 699 | class generic_error_domain : public error_domain 700 | { 701 | public: 702 | 703 | constexpr generic_error_domain() noexcept 704 | : error_domain{{0x574ce0d940b64a2bULL, 0xa7c4438dd858c9cfULL}} 705 | { } 706 | 707 | virtual string_ref name() const noexcept override 708 | { 709 | return "generic domain"; 710 | } 711 | 712 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 713 | 714 | virtual string_ref message(const error&) const noexcept override; 715 | }; 716 | 717 | STDX_LEGACY_INLINE_CONSTEXPR generic_error_domain generic_domain {}; 718 | 719 | class error 720 | { 721 | using erased_type = detail::erased_error; 722 | 723 | constexpr error(detail::error_copy_construct_t, error_value<> v, const error_domain* d) noexcept 724 | : m_domain(d), m_value(v.m_value) 725 | { } 726 | 727 | constexpr error(detail::error_move_construct_t, error_value<> v, const error_domain* d) noexcept 728 | : m_domain(d), m_value(std::move(v.m_value)) 729 | { } 730 | 731 | public: 732 | 733 | constexpr error() noexcept : m_domain(&generic_domain), m_value{} 734 | { } 735 | 736 | constexpr error(const error& e) 737 | : error(detail::error_copy_construct_t{}, e.m_domain->copy(e), e.m_domain) 738 | { } 739 | 740 | constexpr error(error&& e) 741 | : error(detail::error_move_construct_t{}, e.m_domain->move(std::move(e)), e.m_domain) 742 | { } 743 | 744 | template < 745 | class T, 746 | class = std::enable_if_t< 747 | detail::error_type_is_erasable::value 748 | > 749 | > 750 | constexpr error(const error_value& v, const error_domain& d) noexcept 751 | : m_domain(&d), m_value(v.value()) 752 | { } 753 | 754 | template < 755 | class T, 756 | class = std::enable_if_t< 757 | detail::error_type_is_erasable::value 758 | > 759 | > 760 | constexpr error(error_value&& v, const error_domain& d) noexcept 761 | : m_domain(&d), m_value(static_cast(v.value())) 762 | { } 763 | 764 | constexpr error(error_value<> v, const error_domain& d) noexcept 765 | : m_domain(&d), m_value(v.m_value) 766 | { } 767 | 768 | template < 769 | class A, 770 | class... Args, 771 | class = std::enable_if_t< 772 | detail::construct_error_disjunction_t::value 773 | > 774 | > 775 | constexpr error(A&& a, Args&&... args) noexcept( 776 | noexcept( 777 | detail::construct_error_impl( 778 | std::declval>(), 779 | std::forward(a), 780 | std::forward(args)... 781 | ) 782 | ) 783 | ) 784 | : 785 | error( 786 | detail::construct_error_impl( 787 | detail::construct_error_disjunction_t{}, 788 | std::forward(a), 789 | std::forward(args)... 790 | ) 791 | ) 792 | { } 793 | 794 | error& operator = (const error& e) 795 | { 796 | error_value<> v = e.domain().copy(e); 797 | domain().destroy(*this); 798 | m_domain = e.m_domain; 799 | m_value = v.m_value; 800 | return *this; 801 | } 802 | 803 | error& operator = (error&& e) noexcept 804 | { 805 | if (this != &e) 806 | { 807 | error_value<> v = e.domain().move(std::move(e)); 808 | domain().destroy(*this); 809 | m_domain = e.m_domain; 810 | m_value = v.m_value; 811 | } 812 | 813 | return *this; 814 | } 815 | 816 | ~error() noexcept 817 | { 818 | m_domain->destroy(*this); 819 | } 820 | 821 | const error_domain& domain() const noexcept 822 | { 823 | return *m_domain; 824 | } 825 | 826 | string_ref message() const noexcept 827 | { 828 | return domain().message(*this); 829 | } 830 | 831 | [[noreturn]] void throw_exception() const 832 | { 833 | domain().throw_exception(*this); 834 | abort(); 835 | } 836 | 837 | friend class error_domain; 838 | friend struct detail::error_move_access; 839 | friend struct detail::error_ref_access; 840 | friend struct detail::error_cref_access; 841 | 842 | private: 843 | 844 | const error_domain* m_domain; 845 | erased_type m_value; 846 | }; 847 | 848 | inline bool operator == (const error& lhs, const error& rhs) noexcept 849 | { 850 | if (lhs.domain().equivalent(lhs, rhs)) return true; 851 | if (rhs.domain().equivalent(rhs, lhs)) return true; 852 | return false; 853 | } 854 | 855 | inline bool operator != (const error& lhs, const error& rhs) noexcept 856 | { 857 | return !(lhs == rhs); 858 | } 859 | 860 | namespace detail { 861 | 862 | struct error_move_access 863 | { 864 | constexpr explicit error_move_access(error&& e) noexcept : m_value(&e.m_value) 865 | { } 866 | 867 | STDX_LEGACY_CONSTEXPR detail::erased_error&& rvalue_ref() noexcept 868 | { 869 | return std::move(*m_value); 870 | } 871 | 872 | detail::erased_error* m_value; 873 | }; 874 | 875 | struct error_ref_access 876 | { 877 | constexpr explicit error_ref_access(error& e) noexcept : m_ptr(&e.m_value) 878 | { } 879 | 880 | detail::erased_error& ref() noexcept { return *m_ptr; } 881 | 882 | detail::erased_error* m_ptr; 883 | }; 884 | 885 | struct error_cref_access 886 | { 887 | constexpr explicit error_cref_access(const error& e) noexcept : m_ptr(&e.m_value) 888 | { } 889 | 890 | constexpr const detail::erased_error& ref() const noexcept { return *m_ptr; } 891 | 892 | const detail::erased_error* m_ptr; 893 | }; 894 | 895 | } // end namespace detail 896 | 897 | template < 898 | class T, 899 | class = void_t< 900 | decltype(detail::error_cast_impl(std::declval())) 901 | > 902 | > 903 | constexpr T error_cast(const error& e) noexcept( 904 | noexcept(detail::error_cast_impl(std::declval())) 905 | ) 906 | { 907 | return detail::error_cast_impl(detail::error_cref_access{e}.ref()); 908 | } 909 | 910 | template < 911 | class T, 912 | class = void_t(std::declval()))> 913 | > 914 | constexpr T error_cast(error&& e) noexcept( 915 | noexcept(detail::error_cast_impl(std::declval())) 916 | ) 917 | { 918 | return detail::error_cast_impl(detail::error_move_access{std::move(e)}.rvalue_ref()); 919 | } 920 | 921 | namespace detail { 922 | 923 | struct default_error_constructors 924 | { 925 | template 926 | static error_value<> copy_constructor(const error& e) noexcept( 927 | std::is_nothrow_copy_constructible::value 928 | && std::is_nothrow_move_constructible::value 929 | ) 930 | { 931 | T value = error_cast(e); 932 | return error_value<>{std::move(value)}; 933 | } 934 | 935 | template 936 | static error_value<> move_constructor(error&& e) noexcept( 937 | std::is_nothrow_move_constructible::value 938 | ) 939 | { 940 | return error_value<>{error_cast(std::move(e))}; 941 | } 942 | 943 | template 944 | static void destructor(error& e) noexcept 945 | { 946 | detail::erased_error& value = error_ref_access{e}.ref(); 947 | stdx::launder(reinterpret_cast(&value.storage))->~T(); 948 | } 949 | 950 | template 951 | constexpr static error_resource_management::copy_constructor copy() noexcept 952 | { 953 | return is_trivially_copy_constructible::value ? 954 | nullptr : ©_constructor; 955 | } 956 | 957 | template 958 | constexpr static error_resource_management::move_constructor move() noexcept 959 | { 960 | return is_trivially_move_constructible::value ? 961 | nullptr : &move_constructor; 962 | } 963 | 964 | template 965 | constexpr static error_resource_management::destructor destroy() noexcept 966 | { 967 | return is_trivially_destructible::value ? 968 | nullptr : &destructor; 969 | } 970 | }; 971 | 972 | } // end namespace detail 973 | 974 | template 975 | struct default_error_resource_management_t : error_resource_management 976 | { 977 | constexpr default_error_resource_management_t() noexcept 978 | : 979 | error_resource_management{ 980 | detail::default_error_constructors::copy(), 981 | detail::default_error_constructors::move(), 982 | detail::default_error_constructors::destroy() 983 | } 984 | {} 985 | }; 986 | 987 | #if defined(STDX_VARIABLE_TEMPLATES) 988 | template 989 | inline constexpr default_error_resource_management_t default_error_resource_management {}; 990 | #endif 991 | 992 | template <> 993 | struct error_traits 994 | { 995 | static std::exception_ptr to_exception(std::errc ec) noexcept 996 | { 997 | return std::make_exception_ptr(std::make_error_code(ec)); 998 | } 999 | 1000 | static error to_error(std::errc ec) noexcept 1001 | { 1002 | return error{error_value{ec}, generic_domain}; 1003 | } 1004 | }; 1005 | 1006 | namespace detail { 1007 | 1008 | struct error_code_wrapper : enable_reference_count 1009 | { 1010 | explicit error_code_wrapper(std::error_code ec) noexcept : code(ec) 1011 | { } 1012 | 1013 | std::error_code code; 1014 | }; 1015 | 1016 | } // end namespace detail 1017 | 1018 | // Error domain mapping to std::error_code 1019 | // 1020 | class error_code_error_domain : public error_domain 1021 | { 1022 | using internal_value_type = intrusive_ptr; 1023 | 1024 | friend class error_traits; 1025 | 1026 | public: 1027 | 1028 | constexpr error_code_error_domain() noexcept 1029 | : 1030 | error_domain{ 1031 | {0x84e99cdcecae4443ULL, 0x9050179b713fd2afULL}, 1032 | default_error_resource_management_t{} 1033 | } 1034 | { } 1035 | 1036 | virtual string_ref name() const noexcept override 1037 | { 1038 | return "std::error_code error domain"; 1039 | } 1040 | 1041 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 1042 | 1043 | virtual string_ref message(const error& e) const noexcept override; 1044 | 1045 | [[noreturn]] virtual void throw_exception(const error& e) const override; 1046 | }; 1047 | 1048 | STDX_LEGACY_INLINE_CONSTEXPR error_code_error_domain error_code_domain {}; 1049 | 1050 | template <> 1051 | struct error_traits 1052 | { 1053 | static std::error_code from_exception(std::exception_ptr e) noexcept 1054 | { 1055 | return error_code_from_exception(std::move(e)); 1056 | } 1057 | 1058 | static std::exception_ptr to_exception(std::error_code ec) noexcept 1059 | { 1060 | return std::make_exception_ptr(std::system_error{ec}); 1061 | } 1062 | 1063 | static error to_error(std::error_code ec) noexcept; 1064 | }; 1065 | 1066 | namespace detail { 1067 | 1068 | template 1069 | struct exception_ptr_wrapper_impl 1070 | { 1071 | struct control_block : enable_reference_count 1072 | { 1073 | explicit control_block(Ptr p) noexcept : ptr_(std::move(p)) 1074 | { } 1075 | 1076 | Ptr ptr_; 1077 | }; 1078 | 1079 | explicit exception_ptr_wrapper_impl(Ptr p) : ptr{new control_block{std::move(p)}} 1080 | { } 1081 | 1082 | Ptr get() noexcept { return ptr ? ptr->ptr_ : Ptr{}; } 1083 | 1084 | intrusive_ptr ptr; 1085 | }; 1086 | 1087 | template 1088 | struct exception_ptr_wrapper_impl 1089 | { 1090 | explicit exception_ptr_wrapper_impl(Ptr p) : ptr{std::move(p)} 1091 | { } 1092 | 1093 | Ptr get() noexcept { return ptr; } 1094 | 1095 | Ptr ptr; 1096 | }; 1097 | 1098 | using exception_ptr_wrapper = exception_ptr_wrapper_impl; 1099 | 1100 | static_assert(sizeof(exception_ptr_wrapper) == sizeof(std::intptr_t), "Internal library error"); 1101 | 1102 | } // end namespace detail 1103 | 1104 | #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 1105 | template <> 1106 | struct is_trivially_relocatable : std::true_type 1107 | { }; 1108 | #endif 1109 | 1110 | // Error domain mapping to std::exception_ptr 1111 | // 1112 | class dynamic_exception_error_domain : public error_domain 1113 | { 1114 | public: 1115 | 1116 | constexpr dynamic_exception_error_domain() noexcept 1117 | : 1118 | error_domain{ 1119 | {0x3c223c0aa3cf45e5ULL, 0x80dac24345cfb9fcULL}, 1120 | default_error_resource_management_t{} 1121 | } 1122 | { } 1123 | 1124 | virtual string_ref name() const noexcept override 1125 | { 1126 | return "dynamic exception domain"; 1127 | } 1128 | 1129 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 1130 | 1131 | virtual string_ref message(const error&) const noexcept override; 1132 | 1133 | [[noreturn]] virtual void throw_exception(const error& e) const override 1134 | { 1135 | assert(e.domain() == *this); 1136 | std::rethrow_exception(error_cast(e).get()); 1137 | } 1138 | }; 1139 | 1140 | STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_error_domain dynamic_exception_domain {}; 1141 | 1142 | // Error domain mapping to dynamic_exception_errc 1143 | // 1144 | class dynamic_exception_code_error_domain : public error_domain 1145 | { 1146 | public: 1147 | 1148 | constexpr dynamic_exception_code_error_domain() noexcept 1149 | : error_domain{{0xa242506c26484677ULL, 0x82365303df25e338ULL}} 1150 | { } 1151 | 1152 | virtual string_ref name() const noexcept override 1153 | { 1154 | return "dynamic exception code domain"; 1155 | } 1156 | 1157 | virtual bool equivalent(const error& lhs, const error& rhs) const noexcept override; 1158 | 1159 | virtual string_ref message(const error&) const noexcept override; 1160 | }; 1161 | 1162 | STDX_LEGACY_INLINE_CONSTEXPR dynamic_exception_code_error_domain dynamic_exception_code_domain {}; 1163 | 1164 | inline error make_error(dynamic_exception_errc code) noexcept 1165 | { 1166 | return error{error_value{code}, dynamic_exception_code_domain}; 1167 | } 1168 | 1169 | struct thrown_dynamic_exception : std::exception 1170 | { 1171 | explicit thrown_dynamic_exception(stdx::error e) noexcept : m_error(e) 1172 | { } 1173 | 1174 | stdx::error error() const noexcept 1175 | { 1176 | return m_error; 1177 | } 1178 | 1179 | private: 1180 | 1181 | stdx::error m_error; 1182 | }; 1183 | 1184 | template <> 1185 | struct error_traits 1186 | { 1187 | static std::exception_ptr from_exception(std::exception_ptr e) noexcept 1188 | { 1189 | return e; 1190 | } 1191 | 1192 | static std::exception_ptr to_exception(std::exception_ptr e) noexcept 1193 | { 1194 | return e; 1195 | } 1196 | 1197 | static error to_error(std::exception_ptr e) noexcept 1198 | { 1199 | return error{ 1200 | error_value{detail::exception_ptr_wrapper{e}}, 1201 | dynamic_exception_domain 1202 | }; 1203 | } 1204 | }; 1205 | 1206 | } // end namespace stdx 1207 | 1208 | namespace std { 1209 | 1210 | template<> 1211 | struct is_error_code_enum : std::true_type 1212 | { }; 1213 | 1214 | } // end namespace std 1215 | 1216 | #endif 1217 | 1218 | -------------------------------------------------------------------------------- /include/intrusive_ptr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_INTRUSIVE_POINTER_HPP 2 | #define STDX_INTRUSIVE_POINTER_HPP 3 | 4 | #include "compiler.hpp" 5 | #include "type_traits.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace stdx { 12 | 13 | class default_intrusive_reference_count; 14 | class default_intrusive_reference_control; 15 | 16 | template < 17 | class T, 18 | class RefCountAccessor = default_intrusive_reference_count, 19 | class Deleter = std::default_delete, 20 | class Pointer = T* 21 | > 22 | class intrusive_ptr; 23 | 24 | using ref_count_t = std::size_t; 25 | 26 | struct enable_reference_count 27 | { 28 | protected: 29 | 30 | constexpr enable_reference_count() noexcept : m_reference_count(1) 31 | { } 32 | 33 | public: 34 | 35 | std::atomic& shared_reference_count() noexcept 36 | { 37 | return m_reference_count; 38 | } 39 | 40 | private: 41 | 42 | std::atomic m_reference_count; 43 | }; 44 | 45 | struct default_intrusive_reference_count 46 | { 47 | template 48 | std::atomic& operator()(Pointer p) const noexcept 49 | { 50 | return p->shared_reference_count(); 51 | } 52 | }; 53 | 54 | namespace detail { 55 | 56 | template 57 | struct pointer_wrapper 58 | { 59 | constexpr pointer_wrapper() noexcept : shared_object(nullptr) 60 | { } 61 | 62 | constexpr explicit pointer_wrapper(Pointer p) noexcept : shared_object(p) 63 | { } 64 | 65 | constexpr pointer_wrapper(const pointer_wrapper& p) noexcept = default; 66 | 67 | pointer_wrapper(pointer_wrapper&& p) noexcept 68 | : shared_object(p.shared_object) 69 | { 70 | p.shared_object = nullptr; 71 | } 72 | 73 | pointer_wrapper& operator = (const pointer_wrapper&) noexcept = default; 74 | 75 | pointer_wrapper& operator = (pointer_wrapper&& p) noexcept 76 | { 77 | shared_object = p.shared_object; 78 | p.shared_object = nullptr; 79 | return *this; 80 | } 81 | 82 | void assign(Pointer ptr) noexcept 83 | { 84 | shared_object = ptr; 85 | } 86 | 87 | Pointer shared_object; 88 | }; 89 | 90 | template < 91 | class T, 92 | class RefCountAccessor, 93 | class Deleter, 94 | class Pointer, 95 | class PointerImplementation 96 | > 97 | class intrusive_ptr_base 98 | { 99 | protected: 100 | 101 | using pointer = Pointer; 102 | using count_type = ref_count_t; 103 | 104 | template 105 | friend class reference_count_base; 106 | 107 | constexpr intrusive_ptr_base() = default; 108 | 109 | constexpr intrusive_ptr_base(pointer p) noexcept 110 | : m_impl(p) 111 | { } 112 | 113 | template 114 | constexpr explicit intrusive_ptr_base( 115 | RefCountAccessorForwardingReference&& f, 116 | std::enable_if_t< 117 | std::is_constructible< 118 | RefCountAccessor, 119 | RefCountAccessorForwardingReference&& 120 | >::value 121 | >* = nullptr 122 | ) 123 | : m_impl(std::forward(f)) 124 | { } 125 | 126 | template 127 | explicit intrusive_ptr_base( 128 | pointer ptr, 129 | RefCountAccessorForwardingReference&& f, 130 | std::enable_if_t< 131 | std::is_constructible< 132 | RefCountAccessor, 133 | RefCountAccessorForwardingReference&& 134 | >::value 135 | >* = nullptr 136 | ) 137 | : m_impl(ptr, std::forward(f)) 138 | { } 139 | 140 | template < 141 | class RefCountAccessorForwardingReference, 142 | class DeleterForwardingReference 143 | > 144 | constexpr explicit intrusive_ptr_base( 145 | RefCountAccessorForwardingReference&& f, 146 | DeleterForwardingReference&& d, 147 | typename std::enable_if< 148 | std::is_constructible< 149 | RefCountAccessor, 150 | RefCountAccessorForwardingReference&& 151 | >::value 152 | && std::is_constructible< 153 | Deleter, 154 | DeleterForwardingReference&& 155 | >::value 156 | >::type* = nullptr 157 | ) 158 | : 159 | m_impl( 160 | std::forward(f), 161 | std::forward(d) 162 | ) 163 | { } 164 | 165 | template < 166 | class RefCountAccessorForwardingReference, 167 | class DeleterForwardingReference 168 | > 169 | explicit intrusive_ptr_base( 170 | pointer ptr, 171 | RefCountAccessorForwardingReference&& f, 172 | DeleterForwardingReference&& d, 173 | std::enable_if_t< 174 | std::is_constructible< 175 | RefCountAccessor, 176 | RefCountAccessorForwardingReference&& 177 | >::value 178 | && std::is_constructible< 179 | Deleter, 180 | DeleterForwardingReference&& 181 | >::value 182 | >* = nullptr 183 | ) 184 | : 185 | m_impl( 186 | ptr, 187 | std::forward(f), 188 | std::forward(d) 189 | ) 190 | { } 191 | 192 | void assign(pointer ptr) noexcept 193 | { 194 | m_impl.assign(ptr); 195 | } 196 | 197 | template < 198 | class RefCountAccessorForwardingReference, 199 | class DeleterForwardingReference 200 | > 201 | void assign( 202 | pointer ptr, 203 | RefCountAccessorForwardingReference&& f, 204 | DeleterForwardingReference&& d 205 | ) 206 | { 207 | m_impl.assign( 208 | ptr, 209 | std::forward(f), 210 | std::forward(d) 211 | ); 212 | } 213 | 214 | void swap(intrusive_ptr_base& other) 215 | { 216 | m_impl.swap(other.m_impl); 217 | } 218 | 219 | struct STDX_MSVC_EMPTY_BASE_CLASSES impl 220 | : 221 | PointerImplementation, 222 | RefCountAccessor, 223 | Deleter 224 | { 225 | constexpr impl() = default; 226 | 227 | explicit impl(pointer ptr) noexcept : PointerImplementation(ptr) 228 | { } 229 | 230 | template < 231 | class RefCountAccess, 232 | class = std::enable_if_t< 233 | std::is_constructible::value 234 | > 235 | > 236 | constexpr explicit impl(RefCountAccess&& f) 237 | : RefCountAccessor(std::forward(f)) 238 | { } 239 | 240 | template < 241 | class RefCountAccess, 242 | class = std::enable_if_t< 243 | std::is_constructible::value 244 | > 245 | > 246 | impl(pointer ptr, RefCountAccess&& f) 247 | : 248 | PointerImplementation(ptr), 249 | RefCountAccessor(std::forward(f)) 250 | { } 251 | 252 | template < 253 | class RefCountAccess, 254 | class D, 255 | class = std::enable_if_t< 256 | std::is_constructible::value 257 | && std::is_constructible::value 258 | > 259 | > 260 | constexpr impl(RefCountAccess&& f, D&& d) 261 | : 262 | RefCountAccessor(std::forward(f)), 263 | Deleter(std::forward(d)) 264 | { } 265 | 266 | template < 267 | class RefCountAccess, 268 | class D, 269 | class = std::enable_if_t< 270 | std::is_constructible::value 271 | && std::is_constructible::value 272 | > 273 | > 274 | impl(pointer ptr, RefCountAccess&& f, D&& d) 275 | : 276 | PointerImplementation(ptr), 277 | RefCountAccessor(std::forward(f)), 278 | Deleter(std::forward(d)) 279 | { } 280 | 281 | impl(const impl&) = default; 282 | impl& operator = (const impl&) = default; 283 | impl(impl&&) = default; 284 | impl& operator = (impl&&) = default; 285 | 286 | Deleter& get_deleter() noexcept 287 | { 288 | return static_cast(*this); 289 | } 290 | 291 | const Deleter& get_deleter() const noexcept 292 | { 293 | return static_cast(*this); 294 | } 295 | 296 | void assign(pointer ptr) noexcept 297 | { 298 | static_cast(*this).assign(ptr); 299 | } 300 | 301 | void assign(std::nullptr_t) noexcept 302 | { 303 | static_cast(*this).assign(nullptr); 304 | } 305 | 306 | void swap(impl& other) 307 | { 308 | std::swap( 309 | static_cast(*this), 310 | static_cast(other) 311 | ); 312 | 313 | std::swap(static_cast(*this), static_cast(other)); 314 | std::swap( 315 | static_cast(this->get_deleter()), 316 | static_cast(other.get_deleter()) 317 | ); 318 | } 319 | }; 320 | 321 | impl m_impl; 322 | 323 | void increment_shared_reference_count( 324 | std::memory_order order = std::memory_order_relaxed 325 | ) const noexcept 326 | { 327 | if (ptr()) ref_count_func()(ptr()).fetch_add(1, order); 328 | } 329 | 330 | void decrement_shared_reference_count() noexcept 331 | { 332 | if (ptr()) 333 | { 334 | if (ref_count_func()(ptr()).fetch_sub(1, std::memory_order_release) == 1) 335 | { 336 | std::atomic_thread_fence(std::memory_order_acquire); 337 | invoke_deleter(ptr()); 338 | } 339 | } 340 | } 341 | 342 | // ----- accessors and modifiers 343 | 344 | pointer& ptr() noexcept 345 | { 346 | return static_cast(m_impl).shared_object; 347 | } 348 | 349 | constexpr pointer ptr() const noexcept 350 | { 351 | return static_cast(m_impl).shared_object; 352 | } 353 | 354 | RefCountAccessor& ref_count_func() noexcept 355 | { 356 | return static_cast(m_impl); 357 | } 358 | 359 | const RefCountAccessor& ref_count_func() const noexcept 360 | { 361 | return static_cast(m_impl); 362 | } 363 | 364 | Deleter& deleter() noexcept 365 | { 366 | return m_impl.get_deleter(); 367 | } 368 | 369 | const Deleter& deleter() const noexcept 370 | { 371 | return m_impl.get_deleter(); 372 | } 373 | 374 | intrusive_ptr< 375 | T, 376 | RefCountAccessor, 377 | Deleter, 378 | Pointer 379 | > make_intrusive_pointer(pointer p) const noexcept 380 | { 381 | return intrusive_ptr{ 382 | p, 383 | ref_count_func(), 384 | deleter() 385 | }; 386 | } 387 | 388 | private: 389 | 390 | void invoke_deleter(pointer p) 391 | { 392 | m_impl.get_deleter()(p); 393 | } 394 | 395 | void invoke_deleter(pointer p) const 396 | { 397 | m_impl.get_deleter()(p); 398 | } 399 | 400 | template 401 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) 402 | { 403 | maybe_delete_shared_object(p, m_impl.get_deleter(), d); 404 | } 405 | 406 | template 407 | void invoke_deleter(pointer p, WeakReferenceCountDescriptor* d) const 408 | { 409 | maybe_delete_shared_object(p, m_impl.get_deleter(), d); 410 | } 411 | }; 412 | 413 | } // end namespace detail 414 | 415 | template < 416 | class T, 417 | class RefCountAccessor, 418 | class Deleter, 419 | class Pointer 420 | > 421 | class STDX_TRIVIALLY_RELOCATABLE intrusive_ptr 422 | : 423 | public detail::intrusive_ptr_base< 424 | T, 425 | RefCountAccessor, 426 | Deleter, 427 | Pointer, 428 | detail::pointer_wrapper 429 | > 430 | { 431 | using base_type = detail::intrusive_ptr_base< 432 | T, 433 | RefCountAccessor, 434 | Deleter, 435 | Pointer, 436 | detail::pointer_wrapper 437 | >; 438 | 439 | public: 440 | 441 | using pointer = Pointer; 442 | using element_type = T; 443 | using ref_count_accessor = RefCountAccessor; 444 | using deleter_type = Deleter; 445 | using count_type = typename base_type::count_type; 446 | 447 | constexpr intrusive_ptr() noexcept : base_type() 448 | { } 449 | 450 | constexpr intrusive_ptr(std::nullptr_t) noexcept : base_type() 451 | { } 452 | 453 | template 454 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f) 455 | : base_type(std::forward(f)) 456 | { } 457 | 458 | template 459 | constexpr intrusive_ptr(std::nullptr_t, RefCountAccess&& f, D&& d) 460 | : base_type(std::forward(f), std::forward(d)) 461 | { } 462 | 463 | constexpr explicit intrusive_ptr(Pointer ptr) noexcept 464 | : base_type(ptr) 465 | { 466 | // reference count must initially be >= 1 467 | } 468 | 469 | template 470 | intrusive_ptr(Pointer ptr, RefCountAccess&& f) noexcept 471 | : base_type(ptr, std::forward(f)) 472 | { 473 | // reference count must initially be >= 1 474 | } 475 | 476 | template 477 | intrusive_ptr(Pointer ptr, RefCountAccess&& f, D&& d) noexcept 478 | : 479 | base_type( 480 | ptr, 481 | std::forward(f), 482 | std::forward(d) 483 | ) 484 | { 485 | // reference count must initially be >= 1 486 | } 487 | 488 | // Copy constructor 489 | // 490 | intrusive_ptr(const intrusive_ptr& rhs) noexcept 491 | : base_type(rhs) 492 | { 493 | this->increment_shared_reference_count(); 494 | } 495 | 496 | // Converting copy-constructor 497 | // 498 | template < 499 | class Y, 500 | class Ptr, 501 | class = std::enable_if_t::value> 502 | > 503 | intrusive_ptr(const intrusive_ptr& rhs) noexcept 504 | : 505 | base_type( 506 | rhs.get(), 507 | rhs.ref_count_func(), 508 | rhs.get_deleter() 509 | ) 510 | { 511 | this->increment_shared_reference_count(); 512 | } 513 | 514 | // Move constructor 515 | // 516 | intrusive_ptr(intrusive_ptr&& rhs) noexcept 517 | : base_type(std::move(rhs)) 518 | { } 519 | 520 | // Copy assignment 521 | // 522 | intrusive_ptr& operator = (const intrusive_ptr& rhs) noexcept 523 | { 524 | rhs.increment_shared_reference_count(); 525 | this->decrement_shared_reference_count(); 526 | 527 | static_cast(*this) = static_cast(rhs); 528 | return *this; 529 | } 530 | 531 | // Move assignment 532 | // 533 | intrusive_ptr& operator = (intrusive_ptr&& rhs) noexcept 534 | { 535 | if (this != std::addressof(rhs)) 536 | { 537 | this->decrement_shared_reference_count(); 538 | static_cast(*this) = std::move(static_cast(rhs)); 539 | } 540 | 541 | return *this; 542 | } 543 | 544 | ~intrusive_ptr() noexcept 545 | { 546 | this->decrement_shared_reference_count(); 547 | } 548 | 549 | void reset() noexcept 550 | { 551 | this->decrement_shared_reference_count(); 552 | this->assign(nullptr); 553 | } 554 | 555 | void reset(std::nullptr_t) noexcept 556 | { 557 | reset(); 558 | } 559 | 560 | void reset(Pointer ptr) noexcept 561 | { 562 | if (this->ptr() != ptr) 563 | { 564 | this->decrement_shared_reference_count(); 565 | this->assign(ptr); 566 | this->increment_shared_reference_count(); 567 | } 568 | } 569 | 570 | void swap(intrusive_ptr& other) noexcept 571 | { 572 | if (this->get() != other.get()) 573 | { 574 | base_type::swap(other); 575 | } 576 | } 577 | 578 | pointer get() const noexcept 579 | { 580 | return this->ptr(); 581 | } 582 | 583 | element_type& operator * () const noexcept 584 | { 585 | return *this->get(); 586 | } 587 | 588 | pointer operator -> () const noexcept 589 | { 590 | return this->get(); 591 | } 592 | 593 | count_type use_count() const noexcept 594 | { 595 | return this->ref_count_func()(get()).load(std::memory_order_acquire); 596 | } 597 | 598 | explicit operator bool() const noexcept 599 | { 600 | return static_cast(this->get()); 601 | } 602 | 603 | Deleter get_deleter() noexcept 604 | { 605 | return this->deleter(); 606 | } 607 | 608 | const Deleter& get_deleter() const noexcept 609 | { 610 | return this->deleter(); 611 | } 612 | 613 | RefCountAccessor ref_count_access() noexcept 614 | { 615 | return this->ref_count_func(); 616 | } 617 | 618 | const RefCountAccessor& ref_count_access() const noexcept 619 | { 620 | return this->ref_count_func(); 621 | } 622 | 623 | private: 624 | 625 | template 626 | friend class intrusive_ptr; 627 | }; 628 | 629 | // -------------- Global equality operators 630 | // 631 | template 632 | bool operator == ( 633 | const intrusive_ptr& lhs, 634 | const intrusive_ptr& rhs 635 | ) noexcept 636 | { 637 | return lhs.get() == rhs.get(); 638 | } 639 | 640 | template 641 | bool operator != ( 642 | const intrusive_ptr& lhs, 643 | const intrusive_ptr& rhs 644 | ) noexcept 645 | { 646 | return !(lhs == rhs); 647 | } 648 | 649 | template 650 | bool operator < ( 651 | const intrusive_ptr& lhs, 652 | const intrusive_ptr& rhs 653 | ) noexcept 654 | { 655 | using pointer1 = typename intrusive_ptr::pointer; 656 | using pointer2 = typename intrusive_ptr::pointer; 657 | using common_type = typename std::common_type::type; 658 | 659 | return std::less{}(lhs.get(), rhs.get()); 660 | } 661 | 662 | template 663 | bool operator > ( 664 | const intrusive_ptr& lhs, 665 | const intrusive_ptr& rhs 666 | ) noexcept 667 | { 668 | return rhs < lhs; 669 | } 670 | 671 | template 672 | bool operator <= ( 673 | const intrusive_ptr& lhs, 674 | const intrusive_ptr& rhs 675 | ) noexcept 676 | { 677 | return !(rhs < lhs); 678 | } 679 | 680 | template 681 | bool operator >= ( 682 | const intrusive_ptr& lhs, 683 | const intrusive_ptr& rhs 684 | ) noexcept 685 | { 686 | return !(lhs < rhs); 687 | } 688 | 689 | template 690 | bool operator == (const intrusive_ptr& lhs, std::nullptr_t) noexcept 691 | { 692 | return !lhs; 693 | } 694 | 695 | template 696 | bool operator == (std::nullptr_t, const intrusive_ptr& rhs) noexcept 697 | { 698 | return !rhs; 699 | } 700 | 701 | template 702 | bool operator != (const intrusive_ptr& lhs, std::nullptr_t) noexcept 703 | { 704 | return bool(lhs); 705 | } 706 | 707 | template 708 | bool operator != (std::nullptr_t, const intrusive_ptr& rhs) noexcept 709 | { 710 | return bool(rhs); 711 | } 712 | 713 | template 714 | bool operator < (const intrusive_ptr& lhs, std::nullptr_t) noexcept 715 | { 716 | using pointer = typename intrusive_ptr::pointer; 717 | return std::less{}(lhs.get(), nullptr); 718 | } 719 | 720 | template 721 | bool operator < (std::nullptr_t, const intrusive_ptr& rhs) noexcept 722 | { 723 | using pointer = typename intrusive_ptr::pointer; 724 | return std::less{}(nullptr, rhs.get()); 725 | } 726 | 727 | template 728 | bool operator > (const intrusive_ptr& lhs, std::nullptr_t) noexcept 729 | { 730 | return (nullptr < lhs); 731 | } 732 | 733 | template 734 | bool operator > (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 735 | { 736 | return (rhs < nullptr); 737 | } 738 | 739 | template 740 | bool operator <= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept 741 | { 742 | return !(nullptr < lhs); 743 | } 744 | 745 | template 746 | bool operator <= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 747 | { 748 | return !(rhs < nullptr); 749 | } 750 | 751 | template 752 | bool operator >= (const intrusive_ptr& lhs, std::nullptr_t rhs) noexcept 753 | { 754 | return !(lhs < nullptr); 755 | } 756 | 757 | template 758 | bool operator >= (std::nullptr_t lhs, const intrusive_ptr& rhs) noexcept 759 | { 760 | return !(nullptr < rhs); 761 | } 762 | 763 | template 764 | void swap(intrusive_ptr& lhs, intrusive_ptr& rhs) noexcept 765 | { 766 | lhs.swap(rhs); 767 | } 768 | 769 | #ifdef STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 770 | // -------- specialization for is_trivially_relocatable 771 | // 772 | template 773 | struct is_trivially_relocatable> : std::true_type 774 | { }; 775 | #endif 776 | 777 | } // end namespace stdx 778 | 779 | #endif // include guard 780 | 781 | 782 | -------------------------------------------------------------------------------- /include/launder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_LAUNDER_HPP 2 | #define STDX_LAUNDER_HPP 3 | 4 | #include 5 | 6 | #include "compiler.hpp" 7 | 8 | namespace stdx { 9 | 10 | #if __cplusplus >= 201703L 11 | #if defined(__cpp_lib_launder) 12 | #define STDX_HAVE_NATIVE_LAUNDER 1 13 | using std::launder; 14 | #elif defined(STDX_CLANG_COMPILER) 15 | #if __has_builtin(__builtin_launder) 16 | #define STDX_HAVE_NATIVE_LAUNDER 1 17 | template 18 | constexpr T* launder(T* p) noexcept 19 | { 20 | return __builtin_launder(p); 21 | } 22 | #endif 23 | #endif 24 | #endif 25 | 26 | #if !defined(STDX_HAVE_NATIVE_LAUNDER) 27 | template 28 | constexpr T* launder(T* p) noexcept 29 | { 30 | return p; 31 | } 32 | #endif 33 | 34 | } // end namespace stdx 35 | 36 | #endif 37 | 38 | 39 | -------------------------------------------------------------------------------- /include/string_ref.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_STRING_REF_HPP 2 | #define STDX_STRING_REF_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace stdx { 9 | 10 | class string_ref; 11 | 12 | namespace detail { 13 | 14 | constexpr const char* cstring_null_scan(const char* s) noexcept 15 | { 16 | return *s ? cstring_null_scan(s + 1) : s; 17 | } 18 | 19 | } // end namespace detail 20 | 21 | class string_ref 22 | { 23 | protected: 24 | 25 | class state_type; 26 | 27 | public: 28 | 29 | using value_type = const char; 30 | using size_type = std::size_t; 31 | using pointer = const char*; 32 | using const_pointer = const char*; 33 | using iterator = const char*; 34 | using const_iterator = const char*; 35 | 36 | struct resource_management 37 | { 38 | using copy_constructor = state_type(*)(const string_ref&); 39 | using move_constructor = state_type(*)(string_ref&&); 40 | using destructor = void(*)(string_ref&); 41 | 42 | constexpr resource_management() noexcept 43 | : copy{nullptr}, move{nullptr}, destroy{nullptr} 44 | { } 45 | 46 | constexpr resource_management( 47 | copy_constructor cctor, 48 | move_constructor mctor, 49 | destructor dtor 50 | ) noexcept 51 | : copy{cctor}, move{mctor}, destroy{dtor} 52 | { } 53 | 54 | copy_constructor copy; 55 | move_constructor move; 56 | destructor destroy; 57 | }; 58 | 59 | constexpr string_ref() noexcept : m_begin(nullptr), m_end(nullptr), context{} 60 | { } 61 | 62 | constexpr string_ref(const char* beg) noexcept 63 | : m_begin(beg), m_end(detail::cstring_null_scan(beg)), context{} 64 | { } 65 | 66 | constexpr string_ref(const char* beg, const char* e) noexcept 67 | : m_begin(beg), m_end(e), context{} 68 | { } 69 | 70 | constexpr string_ref(const char* beg, resource_management rm) noexcept 71 | : 72 | m_begin(beg), 73 | m_end(detail::cstring_null_scan(beg)), 74 | m_resource_management(rm), 75 | context{} 76 | { } 77 | 78 | constexpr string_ref(const char* beg, const char* e, resource_management rm) noexcept 79 | : m_begin(beg), m_end(e), m_resource_management(rm), context{} 80 | { } 81 | 82 | constexpr string_ref( 83 | const char* beg, 84 | const char* e, 85 | resource_management rm, 86 | void* ctx 87 | ) noexcept 88 | : m_begin(beg), m_end(e), m_resource_management(rm), context{ctx} 89 | { } 90 | 91 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(const string_ref& s) 92 | : string_ref{s.m_resource_management.copy ? s.m_resource_management.copy(s) : s.state()} 93 | { } 94 | 95 | STDX_GCC7_WORKAROUND_CONSTEXPR string_ref(string_ref&& s) 96 | : 97 | string_ref{ 98 | s.m_resource_management.move ? 99 | s.m_resource_management.move(std::move(s)) : s.state() 100 | } 101 | { } 102 | 103 | string_ref& operator = (const string_ref& s) 104 | { 105 | string_ref tmp = s; 106 | *this = std::move(tmp); 107 | return *this; 108 | } 109 | 110 | string_ref& operator = (string_ref&& s) 111 | { 112 | if (this != &s) 113 | { 114 | if (m_resource_management.destroy) m_resource_management.destroy(*this); 115 | 116 | // This is legal because of the common initial sequence and the fact 117 | // that any type erased object must be trivially relocatable. 118 | *this = string_ref_state_union{std::move(s)}.state; 119 | } 120 | 121 | return *this; 122 | } 123 | 124 | ~string_ref() noexcept 125 | { 126 | if (m_resource_management.destroy) m_resource_management.destroy(*this); 127 | } 128 | 129 | bool empty() const noexcept { return m_begin == m_end; } 130 | 131 | size_type size() const noexcept { return m_end - m_begin; } 132 | 133 | const_pointer data() const noexcept { return m_begin; } 134 | 135 | iterator begin() noexcept { return m_begin; } 136 | 137 | iterator end() noexcept { return m_end; } 138 | 139 | const_iterator begin() const noexcept { return m_begin; } 140 | 141 | const_iterator end() const noexcept { return m_end; } 142 | 143 | const_iterator cbegin() const noexcept { return m_begin; } 144 | 145 | const_iterator cend() const noexcept { return m_end; } 146 | 147 | protected: 148 | 149 | struct state_type 150 | { 151 | pointer m_begin; 152 | pointer m_end; 153 | resource_management m_resource_management; 154 | void* context; 155 | }; 156 | 157 | state_type state() const noexcept 158 | { 159 | return state_type{m_begin, m_end, m_resource_management, context}; 160 | } 161 | 162 | constexpr explicit string_ref(const state_type& s) noexcept 163 | : 164 | m_begin(s.m_begin), 165 | m_end(s.m_end), 166 | m_resource_management(s.m_resource_management), 167 | context(s.context) 168 | { } 169 | 170 | void clear() noexcept 171 | { 172 | m_begin = nullptr; 173 | m_end = nullptr; 174 | } 175 | 176 | template 177 | union string_ref_state_union_type 178 | { 179 | explicit string_ref_state_union_type(StringRef&& s) : str(std::move(s)) 180 | { } 181 | 182 | ~string_ref_state_union_type() noexcept {} 183 | 184 | StringRef str; 185 | state_type state; 186 | }; 187 | 188 | using string_ref_state_union = string_ref_state_union_type; 189 | 190 | void operator = (const state_type& s) noexcept 191 | { 192 | m_begin = s.m_begin; 193 | m_end = s.m_end; 194 | m_resource_management = s.m_resource_management; 195 | context = s.context; 196 | } 197 | 198 | pointer m_begin; 199 | pointer m_end; 200 | resource_management m_resource_management; 201 | void* context; 202 | }; 203 | 204 | inline bool operator == (const string_ref& lhs, const string_ref& rhs) noexcept 205 | { 206 | return (lhs.size() == rhs.size()) && (std::memcmp(lhs.data(), rhs.data(), lhs.size()) == 0); 207 | } 208 | 209 | inline bool operator != (const string_ref& lhs, const string_ref& rhs) noexcept 210 | { 211 | return !(lhs == rhs); 212 | } 213 | 214 | inline bool operator < (const string_ref& lhs, const string_ref& rhs) noexcept 215 | { 216 | const std::size_t sz = (lhs.size() < rhs.size()) ? lhs.size() : rhs.size(); 217 | int result = std::memcmp(lhs.data(), rhs.data(), sz); 218 | if (result == 0) return lhs.size() < rhs.size(); 219 | return result < 0; 220 | } 221 | 222 | inline bool operator > (const string_ref& lhs, const string_ref& rhs) noexcept 223 | { 224 | return rhs < lhs; 225 | } 226 | 227 | inline bool operator <= (const string_ref& lhs, const string_ref& rhs) noexcept 228 | { 229 | return !(lhs > rhs); 230 | } 231 | 232 | inline bool operator >= (const string_ref& lhs, const string_ref& rhs) noexcept 233 | { 234 | return !(lhs < rhs); 235 | } 236 | 237 | // Reference-counted allocated string 238 | // 239 | class shared_string_ref : public string_ref 240 | { 241 | struct string_arena_base 242 | { 243 | mutable std::atomic ref_count; 244 | std::size_t length; 245 | }; 246 | 247 | struct string_arena : string_arena_base 248 | { 249 | constexpr explicit string_arena(std::size_t length) noexcept 250 | : string_arena_base{{1}, length} 251 | { } 252 | 253 | constexpr static std::size_t header_size() noexcept 254 | { 255 | return sizeof(string_arena); 256 | } 257 | 258 | char* data() noexcept 259 | { 260 | return reinterpret_cast(this) + header_size(); 261 | } 262 | 263 | const char* data() const noexcept 264 | { 265 | return reinterpret_cast(this) + header_size(); 266 | } 267 | 268 | const char* begin() const noexcept { return data(); } 269 | 270 | const char* end() const noexcept { return data() + length; } 271 | }; 272 | 273 | string_ref allocate_string_ref(const char* s, std::size_t length) 274 | { 275 | const std::size_t arena_size = string_arena::header_size() + length; 276 | char* buf = static_cast(::operator new(arena_size)); 277 | string_arena* a = new (buf) string_arena{length}; 278 | std::memcpy(a->data(), s, length); 279 | return shared_string_ref{a}; 280 | } 281 | 282 | explicit shared_string_ref(string_arena* a) noexcept 283 | : 284 | string_ref{ 285 | a->begin(), 286 | a->end(), 287 | string_ref::resource_management{©_construct, &move_construct, &destroy}, 288 | a 289 | } 290 | { } 291 | 292 | const string_arena_base* get_arena() const noexcept 293 | { 294 | return static_cast(this->context); 295 | } 296 | 297 | string_arena_base* get_arena() noexcept 298 | { 299 | return static_cast(this->context); 300 | } 301 | 302 | static string_ref::state_type copy_construct(const string_ref& base) noexcept 303 | { 304 | const shared_string_ref& s = static_cast(base); 305 | const string_arena_base* a = s.get_arena(); 306 | if (a) a->ref_count.fetch_add(1, std::memory_order_relaxed); 307 | return s.state(); 308 | } 309 | 310 | static string_ref::state_type move_construct(string_ref&& base) noexcept 311 | { 312 | shared_string_ref& s = static_cast(base); 313 | auto st = s.state(); 314 | s.context = nullptr; 315 | s.clear(); 316 | return st; 317 | } 318 | 319 | static void destroy(string_ref& base) noexcept 320 | { 321 | shared_string_ref& s = static_cast(base); 322 | string_arena* a = static_cast(s.get_arena()); 323 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1)) 324 | { 325 | std::atomic_thread_fence(std::memory_order_acquire); 326 | ::operator delete(a); 327 | } 328 | } 329 | 330 | template 331 | struct allocated_string_arena : string_arena_base 332 | { 333 | constexpr allocated_string_arena(const Allocator& alloc, std::size_t length) noexcept 334 | : string_arena_base{{1}, length}, allocator(alloc) 335 | { } 336 | 337 | constexpr static std::size_t header_size() noexcept 338 | { 339 | return sizeof(allocated_string_arena); 340 | } 341 | 342 | char* data() noexcept 343 | { 344 | return reinterpret_cast(this) + header_size(); 345 | } 346 | 347 | const char* data() const noexcept 348 | { 349 | return reinterpret_cast(this) + header_size(); 350 | } 351 | 352 | const char* begin() const noexcept { return data(); } 353 | 354 | const char* end() const noexcept { return data() + length; } 355 | 356 | std::size_t allocated_size() const noexcept { return header_size() + length; } 357 | 358 | Allocator allocator; 359 | }; 360 | 361 | template 362 | explicit shared_string_ref(allocated_string_arena* a) noexcept 363 | : 364 | string_ref{ 365 | a->begin(), 366 | a->end(), 367 | string_ref::resource_management{ 368 | ©_construct, 369 | &move_construct, 370 | &allocator_destroy 371 | }, 372 | a 373 | } 374 | { } 375 | 376 | template 377 | string_ref allocate_string_ref( 378 | const Allocator& allocator, 379 | const char* s, 380 | std::size_t length 381 | ) 382 | { 383 | using allocator_type = typename std::allocator_traits< 384 | Allocator 385 | >::template rebind_alloc; 386 | using arena_type = allocated_string_arena; 387 | 388 | allocator_type alloc{allocator}; 389 | const std::size_t arena_size = arena_type::header_size() + length; 390 | char* buf = alloc.allocate(arena_size); 391 | arena_type* a = new (buf) arena_type{alloc, length}; 392 | std::memcpy(a->data(), s, length); 393 | return shared_string_ref{a}; 394 | } 395 | 396 | template 397 | static void allocator_destroy(string_ref& base) noexcept 398 | { 399 | using arena_type = allocated_string_arena; 400 | 401 | shared_string_ref& s = static_cast(base); 402 | arena_type* a = static_cast(s.get_arena()); 403 | if (a && (a->ref_count.fetch_sub(1, std::memory_order_release) == 1)) 404 | { 405 | std::atomic_thread_fence(std::memory_order_acquire); 406 | 407 | Allocator alloc = std::move(a->allocator); 408 | const std::size_t allocated_size = a->allocated_size(); 409 | a->~arena_type(); 410 | alloc.deallocate(reinterpret_cast(a), allocated_size); 411 | } 412 | } 413 | 414 | public: 415 | 416 | shared_string_ref(const char* beg) 417 | : string_ref{allocate_string_ref(beg, detail::cstring_null_scan(beg) - beg)} 418 | { } 419 | 420 | shared_string_ref(const char* beg, const char* end) 421 | : string_ref{allocate_string_ref(beg, end - beg)} 422 | { } 423 | 424 | template 425 | shared_string_ref(const Allocator& alloc, const char* beg) 426 | : string_ref{allocate_string_ref(alloc, beg, detail::cstring_null_scan(beg) - beg)} 427 | { } 428 | 429 | template 430 | shared_string_ref(const Allocator& alloc, const char* beg, const char* end) 431 | : string_ref{allocate_string_ref(alloc, beg, end - beg)} 432 | { } 433 | 434 | std::size_t use_count() const noexcept 435 | { 436 | const string_arena_base* a = get_arena(); 437 | return a ? a->ref_count.load(std::memory_order_acquire) : 0; 438 | } 439 | }; 440 | 441 | } // end namespace stdx 442 | 443 | #endif 444 | 445 | 446 | -------------------------------------------------------------------------------- /include/type_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef STDX_TYPE_TRAITS_HPP 2 | #define STDX_TYPE_TRAITS_HPP 3 | 4 | #include 5 | #include "compiler.hpp" 6 | 7 | namespace stdx { 8 | 9 | // Implementation of std::void_t for use with pre-C++17 compilers. 10 | // 11 | namespace detail { 12 | 13 | template 14 | struct void_t_impl 15 | { 16 | using type = void; 17 | }; 18 | 19 | } // end namespace detail 20 | 21 | template 22 | using void_t = typename detail::void_t_impl::type; 23 | 24 | template 25 | struct dependent_type 26 | { 27 | using type = U; 28 | }; 29 | 30 | template 31 | using dependent_type_t = typename dependent_type::type; 32 | 33 | template 34 | struct disjunction : std::false_type 35 | { }; 36 | 37 | template 38 | struct disjunction : B1 39 | { }; 40 | 41 | template 42 | struct disjunction 43 | : 44 | std::conditional_t< 45 | bool(B1::value), 46 | B1, 47 | disjunction 48 | > 49 | { }; 50 | 51 | struct sentinel_type 52 | { 53 | static constexpr bool value = true; 54 | using type = void; 55 | }; 56 | 57 | template 58 | struct bool_constant : std::integral_constant 59 | { }; 60 | 61 | // Implementation of std::remove_cvref for use with pre-C++20 compilers. 62 | // 63 | template 64 | struct remove_cvref 65 | { 66 | using type = std::remove_cv_t>; 67 | }; 68 | 69 | template 70 | using remove_cvref_t = typename remove_cvref::type; 71 | 72 | #if defined(STDX_GCC_COMPILER) 73 | // Support missing functionality on older compilers (<= gcc 4.7) 74 | // 75 | #if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 7) 76 | // Map the old incorrect type-trait names to the newer correct ones 77 | template 78 | using is_trivially_copyable = std::is_trivial; 79 | 80 | template 81 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor; 82 | 83 | template 84 | using is_trivially_destructible = std::has_trivial_destructor; 85 | #elif (__GNUC__ < 5) 86 | template 87 | using is_trivially_copyable = std::is_trivial; 88 | 89 | template 90 | using is_trivially_copy_constructible = std::has_trivial_copy_constructor; 91 | 92 | template 93 | using is_trivially_destructible = std::is_trivially_destructible; 94 | #else 95 | using std::is_trivially_destructible; 96 | using std::is_trivially_copyable; 97 | using std::is_trivially_copy_constructible; 98 | #endif 99 | #else 100 | using std::is_trivially_destructible; 101 | using std::is_trivially_copyable; 102 | using std::is_trivially_copy_constructible; 103 | #endif 104 | 105 | #if defined(STDX_TRIVIALLY_MOVE_CONSTRUCTIBLE) 106 | template 107 | using is_trivially_move_constructible = std::is_trivially_move_constructible; 108 | #else 109 | template 110 | using is_trivially_move_constructible = is_trivially_copyable; 111 | #endif 112 | 113 | #if defined(__cpp_lib_trivially_relocatable) 114 | using std::is_trivially_relocatable; 115 | #elif defined(__has_builtin) 116 | #if __has_builtin(__is_trivially_relocatable) 117 | template 118 | struct is_trivially_relocatable : std::bool_constant<__is_trivially_relocatable(T)> { }; 119 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 120 | #else 121 | template 122 | struct is_trivially_relocatable : is_trivially_copyable { }; 123 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 124 | #endif 125 | #else 126 | template 127 | struct is_trivially_relocatable : is_trivially_copyable { }; 128 | #define STDX_MUST_SPECIALIZE_IS_TRIVIALLY_RELOCATABLE 129 | #endif 130 | 131 | #if __cplusplus >= 201703L 132 | #define STDX_LEGACY_INLINE_CONSTEXPR inline constexpr 133 | #else 134 | #define STDX_LEGACY_INLINE_CONSTEXPR constexpr 135 | #endif 136 | 137 | } // end namespace stdx 138 | 139 | #endif 140 | 141 | 142 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //#include "include/error.hpp" 11 | //#include "error.cpp" 12 | #include "all_in_one.hpp" 13 | 14 | #ifdef NDEBUG 15 | #error "Cannot compile test with NDEBUG" 16 | #endif 17 | 18 | struct counting_allocator_base 19 | { 20 | static std::atomic instance_count; 21 | }; 22 | 23 | std::atomic counting_allocator_base::instance_count(0); 24 | 25 | template 26 | struct counting_allocator : counting_allocator_base 27 | { 28 | using value_type = T; 29 | 30 | constexpr counting_allocator() noexcept = default; 31 | 32 | template 33 | constexpr counting_allocator(const counting_allocator) noexcept 34 | { } 35 | 36 | T* allocate(std::size_t n) 37 | { 38 | counting_allocator_base::instance_count.fetch_add(n * sizeof(T), std::memory_order_release); 39 | return static_cast(::operator new(n * sizeof(T))); 40 | } 41 | 42 | void deallocate(T* v, std::size_t n) noexcept 43 | { 44 | counting_allocator_base::instance_count.fetch_sub(n * sizeof(T), std::memory_order_release); 45 | ::operator delete(static_cast(v)); 46 | } 47 | }; 48 | 49 | template 50 | constexpr bool operator == (const counting_allocator& lhs, const counting_allocator& rhs) noexcept 51 | { 52 | return true; 53 | } 54 | 55 | template 56 | constexpr bool operator != (const counting_allocator& lhs, const counting_allocator& rhs) noexcept 57 | { 58 | return !(lhs == rhs); 59 | } 60 | 61 | namespace MyStuff { 62 | 63 | struct MyError { }; 64 | 65 | stdx::error make_error(MyError e) noexcept 66 | { 67 | return std::errc::invalid_argument; 68 | } 69 | 70 | } // end namespace MyStuff 71 | 72 | struct ErrorData : stdx::enable_reference_count 73 | { 74 | ErrorData() noexcept : code{} 75 | { } 76 | 77 | ErrorData(const std::string& s, std::uint64_t c) : message(s), code(c) 78 | { } 79 | 80 | std::string message; 81 | std::uint64_t code; 82 | }; 83 | 84 | bool operator == (const ErrorData& lhs, const ErrorData& rhs) noexcept 85 | { 86 | return (lhs.message == rhs.message) && (lhs.code == rhs.code); 87 | } 88 | 89 | bool operator != (const ErrorData& lhs, const ErrorData& rhs) noexcept 90 | { 91 | return !(lhs == rhs); 92 | } 93 | 94 | struct random_device_initializer 95 | { 96 | static std::size_t seed() 97 | { 98 | static std::random_device rd; 99 | return rd(); 100 | } 101 | }; 102 | 103 | std::string random_string(const std::size_t max_string_size = 128) 104 | { 105 | constexpr char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 106 | 107 | static std::mt19937_64 rng(random_device_initializer::seed()); 108 | 109 | const std::size_t str_size = std::max( 110 | 1, 111 | rng() % std::max(1, max_string_size) 112 | ); 113 | 114 | std::string result; 115 | result.reserve(str_size); 116 | for (std::size_t i = 0; i != str_size; ++i) 117 | result.push_back(letters[rng() % (sizeof(letters) - 1)]); 118 | 119 | return result; 120 | } 121 | 122 | std::uint64_t random_integer() 123 | { 124 | static std::mt19937_64 rng(random_device_initializer::seed()); 125 | return rng(); 126 | } 127 | 128 | stdx::intrusive_ptr random_error_data() 129 | { 130 | return stdx::intrusive_ptr{ 131 | new ErrorData{random_string(), random_integer()} 132 | }; 133 | } 134 | 135 | static_assert( 136 | stdx::is_trivially_relocatable>::value, 137 | "FAILz" 138 | ); 139 | 140 | struct MyErrorDomain : stdx::error_domain 141 | { 142 | using value_type = stdx::intrusive_ptr; 143 | 144 | constexpr MyErrorDomain() noexcept 145 | : 146 | stdx::error_domain{ 147 | {0x4725020cb3ca41e5ULL, 0xa335dafe21e65f8cULL}, 148 | stdx::default_error_resource_management_t{} 149 | } 150 | { } 151 | 152 | virtual stdx::string_ref name() const noexcept override 153 | { 154 | return "MyErrorDomain"; 155 | } 156 | 157 | bool equivalent(const stdx::error& lhs, const stdx::error& rhs) const noexcept override 158 | { 159 | assert(lhs.domain() == *this); 160 | if (lhs.domain() == rhs.domain()) 161 | { 162 | return stdx::error_cast(lhs)->code == stdx::error_cast(rhs)->code; 163 | } 164 | 165 | return false; 166 | } 167 | 168 | stdx::string_ref message(const stdx::error& e) const noexcept override 169 | { 170 | value_type edata = stdx::error_cast(e); 171 | return stdx::string_ref{edata->message.data(), edata->message.data() + edata->message.size()}; 172 | } 173 | }; 174 | 175 | constexpr MyErrorDomain my_error_domain {}; 176 | 177 | // custom std::error_category to test interoperation with std::error_code 178 | // 179 | namespace MyLib { 180 | 181 | enum class errc 182 | { 183 | ragtime_error, 184 | invalid_jazz, 185 | missing_pants 186 | }; 187 | 188 | struct MyErrorCategory : std::error_category 189 | { 190 | const char* name() const noexcept override 191 | { 192 | return "MyErrorCategory"; 193 | } 194 | 195 | std::string message(int code) const override 196 | { 197 | switch (static_cast(code)) 198 | { 199 | case errc::ragtime_error: return "Ragtime error"; 200 | case errc::invalid_jazz: return "Invalid jazz"; 201 | case errc::missing_pants: return "Missing pants"; 202 | default:; 203 | return "Unknown error"; 204 | } 205 | } 206 | }; 207 | 208 | MyErrorCategory my_error_category_instance; 209 | 210 | std::error_code make_error_code(errc e) 211 | { 212 | return std::error_code(static_cast(e), my_error_category_instance); 213 | } 214 | 215 | stdx::error make_error(errc e) 216 | { 217 | return make_error_code(e); 218 | } 219 | 220 | } // end namespace MyLib 221 | 222 | namespace std { 223 | 224 | template <> 225 | struct is_error_code_enum : std::true_type 226 | { }; 227 | 228 | } // end namespace std 229 | 230 | void string_ref_test() 231 | { 232 | { 233 | stdx::string_ref s{"abc"}; 234 | assert(s == "abc"); 235 | assert(s.size() == 3); 236 | assert(!s.empty()); 237 | assert(s < "abcd"); 238 | assert(s < "abd"); 239 | assert(s > "abb"); 240 | assert(s > "ab"); 241 | assert(s < "ad"); 242 | assert(s < "b"); 243 | static_assert(std::is_standard_layout::value, "FAILZ"); 244 | static_assert(std::is_standard_layout::value, "FAILZ"); 245 | } 246 | 247 | { 248 | stdx::shared_string_ref s = "xyz"; 249 | assert(s == "xyz"); 250 | assert(s.use_count() == 1); 251 | 252 | { 253 | stdx::shared_string_ref s2 = s; 254 | assert(s.use_count() == 2); 255 | assert(s2.use_count() == 2); 256 | assert(s2 == "xyz"); 257 | 258 | stdx::shared_string_ref s3 = s; 259 | assert(s.use_count() == 3); 260 | assert(s2.use_count() == 3); 261 | assert(s3.use_count() == 3); 262 | assert(s3 == "xyz"); 263 | } 264 | 265 | assert(s.use_count() == 1); 266 | } 267 | 268 | { 269 | stdx::shared_string_ref s = "bEEf"; 270 | assert(s == "bEEf"); 271 | assert(s.use_count() == 1); 272 | 273 | { 274 | stdx::shared_string_ref s1 = s; 275 | assert(s.use_count() == 2); 276 | assert(s1.use_count() == 2); 277 | assert(s1 == "bEEf"); 278 | 279 | stdx::shared_string_ref s2 = s; 280 | assert(s.use_count() == 3); 281 | assert(s2.use_count() == 3); 282 | assert(s2 == "bEEf"); 283 | 284 | stdx::shared_string_ref s3 = std::move(s1); 285 | assert(s1.use_count() == 0); 286 | assert(s1.empty()); 287 | assert(s1 == stdx::string_ref{}); 288 | assert(s1 == ""); 289 | assert(s2.use_count() == 3); 290 | assert(s3.use_count() == 3); 291 | assert(s3 == "bEEf"); 292 | assert(s3 == s2); 293 | assert(s3 == s); 294 | assert(s3 != s1); 295 | assert(!s3.empty()); 296 | assert(s3.size() == 4); 297 | } 298 | 299 | assert(s.use_count() == 1); 300 | } 301 | 302 | { 303 | stdx::shared_string_ref s = "ccc"; 304 | assert(s == "ccc"); 305 | assert(s.use_count() == 1); 306 | 307 | { 308 | stdx::shared_string_ref s2 = s; 309 | assert(s.use_count() == 2); 310 | assert(s2.use_count() == 2); 311 | assert(s2 == "ccc"); 312 | 313 | stdx::shared_string_ref s3 = "xxx"; 314 | assert(s.use_count() == 2); 315 | assert(s2.use_count() == 2); 316 | assert(s3.use_count() == 1); 317 | assert(s3 == "xxx"); 318 | 319 | s3 = s2; 320 | assert(s3 == s2); 321 | assert(s3.use_count() == 3); 322 | assert(s2.use_count() == 3); 323 | assert(s3.data() == s2.data()); 324 | assert(s3 == "ccc"); 325 | 326 | stdx::shared_string_ref s4 = "qqq"; 327 | assert(s4 == "qqq"); 328 | assert(s4.use_count() == 1); 329 | 330 | s4 = std::move(s3); 331 | assert(s4.use_count() == 3); 332 | assert(s4 == "ccc"); 333 | assert(s4.data() == s2.data()); 334 | assert(s4 == s2); 335 | assert(s3.empty()); 336 | assert(s3 == stdx::string_ref{}); 337 | assert(s4 != s3); 338 | } 339 | 340 | assert(s.use_count() == 1); 341 | } 342 | 343 | { 344 | stdx::shared_string_ref s{std::allocator{}, "xyz"}; 345 | assert(s == "xyz"); 346 | assert(s.use_count() == 1); 347 | 348 | { 349 | stdx::shared_string_ref s2 = s; 350 | assert(s.use_count() == 2); 351 | assert(s2.use_count() == 2); 352 | assert(s2 == "xyz"); 353 | 354 | stdx::shared_string_ref s3 = s; 355 | assert(s.use_count() == 3); 356 | assert(s2.use_count() == 3); 357 | assert(s3.use_count() == 3); 358 | assert(s3 == "xyz"); 359 | } 360 | 361 | assert(s.use_count() == 1); 362 | } 363 | 364 | { 365 | stdx::shared_string_ref s{counting_allocator{}, "DFF"}; 366 | assert(s == "DFF"); 367 | assert(s.use_count() == 1); 368 | 369 | assert(counting_allocator_base::instance_count.load() != 0); 370 | 371 | { 372 | stdx::shared_string_ref s2 = s; 373 | assert(s.use_count() == 2); 374 | assert(s2.use_count() == 2); 375 | assert(s2 == "DFF"); 376 | assert(s2 == s); 377 | assert(*std::begin(s2) == 'D'); 378 | assert(*std::prev(std::end(s2)) == 'F'); 379 | 380 | stdx::shared_string_ref s3 = "ZZZ"; 381 | assert(s.use_count() == 2); 382 | assert(s2.use_count() == 2); 383 | assert(s3.use_count() == 1); 384 | assert(s3 == "ZZZ"); 385 | assert(*std::begin(s3) == 'Z'); 386 | 387 | s3 = s2; 388 | assert(s3 == s2); 389 | assert(s3.use_count() == 3); 390 | assert(s2.use_count() == 3); 391 | assert(s3.data() == s2.data()); 392 | assert(s3 == "DFF"); 393 | 394 | stdx::shared_string_ref s4 = "QQQX"; 395 | assert(s4 == "QQQX"); 396 | assert(s4.use_count() == 1); 397 | 398 | s4 = std::move(s3); 399 | assert(s4.use_count() == 3); 400 | assert(s4 == "DFF"); 401 | assert(s4.data() == s2.data()); 402 | assert(s4 == s2); 403 | assert(s3.empty()); 404 | assert(s3 == stdx::string_ref{}); 405 | assert(s4 != s3); 406 | } 407 | 408 | assert(s.use_count() == 1); 409 | } 410 | 411 | assert(counting_allocator_base::instance_count.load() == 0); 412 | 413 | std::cout << "string_ref_test: PASSED!" << std::endl; 414 | } 415 | 416 | void error_test() 417 | { 418 | static_assert(sizeof(stdx::error) == sizeof(void*) * 2, "FAILz"); 419 | static_assert(std::is_standard_layout::value, "FAILz"); 420 | #if defined(__cpp_lib_trivially_relocatable) 421 | static_assert(std::is_trivially_relocatable::value, "FAILz"); 422 | #endif 423 | 424 | // error constructed from std::errc 425 | { 426 | stdx::error e = std::errc::bad_file_descriptor; 427 | assert(e.domain() == stdx::generic_domain); 428 | assert(e.domain().name() == "generic domain"); 429 | assert(e == std::errc::bad_file_descriptor); 430 | assert(e.message() == "Bad file descriptor"); 431 | 432 | stdx::error e2 = MyStuff::MyError{}; 433 | assert(e.domain() == stdx::generic_domain); 434 | 435 | stdx::error e3 = e; 436 | assert(e3.domain() == stdx::generic_domain); 437 | assert(e3.domain().name() == "generic domain"); 438 | assert(e3 == std::errc::bad_file_descriptor); 439 | assert(e3 == e); 440 | assert(e == e3); 441 | assert(e3 != e2); 442 | assert(e3.message() == "Bad file descriptor"); 443 | } 444 | 445 | // error constructed from custom error_domain and error value type 446 | { 447 | using shared_pointer = MyErrorDomain::value_type; 448 | 449 | shared_pointer p = random_error_data(); 450 | assert(p.use_count() == 1); 451 | 452 | { 453 | stdx::error e{p, my_error_domain}; 454 | assert(p.use_count() == 2); 455 | assert(e.domain() == my_error_domain); 456 | } 457 | 458 | assert(p.use_count() == 1); 459 | 460 | { 461 | stdx::error e1{p, my_error_domain}; 462 | assert(p.use_count() == 2); 463 | assert(e1.message() == p->message.data()); 464 | 465 | stdx::error e2 = e1; 466 | assert(e2.domain() == my_error_domain); 467 | assert(p.use_count() == 3); 468 | stdx::error e3 = e2; 469 | assert(e3.domain() == my_error_domain); 470 | assert(p.use_count() == 4); 471 | } 472 | 473 | assert(p.use_count() == 1); 474 | 475 | { 476 | stdx::error e1{p, my_error_domain}; 477 | assert(p.use_count() == 2); 478 | 479 | stdx::error e2 = e1; 480 | assert(e2.domain() == my_error_domain); 481 | assert(p.use_count() == 3); 482 | stdx::error e3 = std::move(e2); 483 | assert(e3.domain() == my_error_domain); 484 | assert(p.use_count() == 3); 485 | stdx::error e4 = std::move(e3); 486 | assert(e4.domain() == my_error_domain); 487 | assert(p.use_count() == 3); 488 | stdx::error e5 = std::move(e2); 489 | assert(e5.domain() == my_error_domain); 490 | assert(p.use_count() == 3); 491 | } 492 | 493 | assert(p.use_count() == 1); 494 | 495 | { 496 | stdx::error e1{p, my_error_domain}; 497 | assert(p.use_count() == 2); 498 | 499 | stdx::error e2 = e1; 500 | assert(p.use_count() == 3); 501 | stdx::error e3 = std::move(e2); 502 | assert(p.use_count() == 3); 503 | stdx::error e4 = std::move(e3); 504 | assert(p.use_count() == 3); 505 | 506 | e2 = std::errc::invalid_argument; 507 | assert(e2.domain() == stdx::generic_domain); 508 | assert(e2 == std::errc::invalid_argument); 509 | assert(p.use_count() == 3); 510 | 511 | e4 = std::errc::invalid_argument; 512 | assert(e4.domain() == stdx::generic_domain); 513 | assert(e4 == std::errc::invalid_argument); 514 | assert(e4 == e2); 515 | assert(p.use_count() == 2); 516 | } 517 | 518 | { 519 | stdx::error e1{p, my_error_domain}; 520 | assert(p.use_count() == 2); 521 | 522 | stdx::error e2 = e1; 523 | assert(p.use_count() == 3); 524 | stdx::error e3 = std::move(e2); 525 | assert(p.use_count() == 3); 526 | stdx::error e4 = std::move(e3); 527 | assert(p.use_count() == 3); 528 | 529 | e2 = std::errc::invalid_argument; 530 | assert(e2.domain() == stdx::generic_domain); 531 | assert(e2 == std::errc::invalid_argument); 532 | assert(p.use_count() == 3); 533 | 534 | e2 = stdx::error{p, my_error_domain}; 535 | assert(e2.domain() == my_error_domain); 536 | assert(e2 != std::errc::invalid_argument); 537 | assert(p.use_count() == 4); 538 | 539 | e3 = e2; 540 | assert(p.use_count() == 5); 541 | e3 = e1; 542 | assert(p.use_count() == 5); 543 | e3 = e3; 544 | assert(p.use_count() == 5); 545 | e3 = std::move(e3); 546 | assert(p.use_count() == 5); 547 | 548 | e3 = std::move(e1); 549 | assert(p.use_count() == 4); 550 | } 551 | 552 | assert(p.use_count() == 1); 553 | } 554 | 555 | // error constructed from std::error_code 556 | { 557 | stdx::error e = std::make_error_code(std::errc::file_too_large); 558 | assert(e.domain() == stdx::generic_domain); 559 | assert(e == std::errc::file_too_large); 560 | assert(e == std::make_error_code(std::errc::file_too_large)); 561 | 562 | e = std::errc::invalid_argument; 563 | assert(e.domain() == stdx::generic_domain); 564 | assert(e == std::errc::invalid_argument); 565 | assert(e == std::make_error_code(std::errc::invalid_argument)); 566 | } 567 | 568 | // error constructed from std::error_code using custom error category 569 | { 570 | stdx::error e = MyLib::make_error_code(MyLib::errc::missing_pants); 571 | assert(e.domain() == stdx::error_code_domain); 572 | assert(e == MyLib::make_error_code(MyLib::errc::missing_pants)); 573 | assert(e == MyLib::errc::missing_pants); 574 | assert(e != MyLib::make_error_code(MyLib::errc::invalid_jazz)); 575 | assert(e != std::errc::invalid_argument); 576 | assert(e != MyLib::errc::ragtime_error); 577 | assert(e.message() == "Missing pants"); 578 | 579 | stdx::error e2 = e; 580 | assert(e2 == e); 581 | assert(e2.domain() == stdx::error_code_domain); 582 | assert(e2 == MyLib::make_error_code(MyLib::errc::missing_pants)); 583 | assert(e2 == MyLib::errc::missing_pants); 584 | assert(e2.message() == "Missing pants"); 585 | 586 | stdx::error e3 = std::move(e2); 587 | assert(e3 == e); 588 | assert(e3.domain() == stdx::error_code_domain); 589 | assert(e3 == MyLib::make_error_code(MyLib::errc::missing_pants)); 590 | assert(e3 == MyLib::errc::missing_pants); 591 | assert(e3.message() == "Missing pants"); 592 | 593 | assert(e2.domain() == stdx::error_code_domain); 594 | assert(e2 != e3); 595 | assert(e2 != MyLib::make_error_code(MyLib::errc::missing_pants)); 596 | 597 | e2 = e3; 598 | assert(e2 == e); 599 | assert(e2.domain() == stdx::error_code_domain); 600 | assert(e2 == MyLib::make_error_code(MyLib::errc::missing_pants)); 601 | assert(e2 == MyLib::errc::missing_pants); 602 | assert(e2.message() == "Missing pants"); 603 | 604 | e2 = MyLib::errc::invalid_jazz; 605 | assert(e2.domain() == stdx::error_code_domain); 606 | assert(e2 == MyLib::make_error_code(MyLib::errc::invalid_jazz)); 607 | assert(e2 == MyLib::errc::invalid_jazz); 608 | assert(e2.message() == "Invalid jazz"); 609 | 610 | e2 = MyLib::errc::ragtime_error; 611 | assert(e2.domain() == stdx::error_code_domain); 612 | assert(e2 == MyLib::make_error_code(MyLib::errc::ragtime_error)); 613 | assert(e2 == MyLib::errc::ragtime_error); 614 | assert(e2.message() == "Ragtime error"); 615 | 616 | e2 = std::make_error_code(std::errc::file_too_large); 617 | assert(e2.domain() == stdx::generic_domain); 618 | assert(e2 == std::errc::file_too_large); 619 | assert(e2 == std::make_error_code(std::errc::file_too_large)); 620 | } 621 | 622 | // error constructed from std::exception_ptr 623 | { 624 | std::exception_ptr eptr = std::make_exception_ptr(std::logic_error{"Invalid pants selection"}); 625 | stdx::error e = eptr; 626 | assert(e.domain() == stdx::dynamic_exception_domain); 627 | assert(e.message() == "Invalid pants selection"); 628 | 629 | try { e.throw_exception(); } 630 | catch (const std::logic_error& ex) 631 | { 632 | assert(stdx::string_ref{ex.what()} == "Invalid pants selection"); 633 | } 634 | 635 | stdx::error e2 = e; 636 | try { e2.throw_exception(); } 637 | catch (const std::logic_error& ex) 638 | { 639 | assert(stdx::string_ref{ex.what()} == "Invalid pants selection"); 640 | } 641 | 642 | assert(e2 == stdx::dynamic_exception_errc::logic_error); 643 | 644 | e2 = std::make_exception_ptr(std::invalid_argument{"Erroneous reticulum"}); 645 | try { e2.throw_exception(); } 646 | catch (const std::invalid_argument& ex) 647 | { 648 | assert(stdx::string_ref{ex.what()} == "Erroneous reticulum"); 649 | } 650 | 651 | assert(e2 == stdx::dynamic_exception_errc::invalid_argument); 652 | assert(e2 == std::errc::invalid_argument); 653 | assert(e2 != stdx::dynamic_exception_errc::domain_error); 654 | assert(e2 != std::errc::bad_file_descriptor); 655 | assert(stdx::dynamic_exception_errc::invalid_argument == e2); 656 | assert(std::errc::invalid_argument == e2); 657 | 658 | e2 = std::make_exception_ptr( 659 | std::system_error{std::make_error_code(std::errc::bad_file_descriptor)} 660 | ); 661 | try { e2.throw_exception(); } 662 | catch (const std::system_error& ex) 663 | { 664 | assert(ex.code() == std::errc::bad_file_descriptor); 665 | assert(stdx::error{std::errc::bad_file_descriptor} == ex.code()); 666 | } 667 | 668 | assert(e2 == std::errc::bad_file_descriptor); 669 | assert(std::errc::bad_file_descriptor == e2); 670 | assert(e2.message() == std::make_error_code(std::errc::bad_file_descriptor).message().c_str()); 671 | } 672 | 673 | std::cout << "stdx::error test: PASSED!" << std::endl; 674 | } 675 | 676 | int main() 677 | { 678 | string_ref_test(); 679 | error_test(); 680 | } 681 | 682 | --------------------------------------------------------------------------------