Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/c++/15/stacktrace
1 // <stacktrace> -*- C++ -*- 2 3 // Copyright The GNU Toolchain Authors. 4 // 5 // This file is part of the GNU ISO C++ Library. This library is free 6 // software; you can redistribute it and/or modify it under the 7 // terms of the GNU General Public License as published by the 8 // Free Software Foundation; either version 3. 9 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 15 // Under Section 7 of GPL version 3, you are granted additional 16 // permissions described in the GCC Runtime Library Exception, version 17 // 3.1, as published by the Free Software Foundation. 18 19 // You should have received a copy of the GNU General Public License and 20 // a copy of the GCC Runtime Library Exception along with this program; 21 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 22 // <http://www.gnu.org/licenses/>. 23 24 #ifndef _GLIBCXX_STACKTRACE 25 #define _GLIBCXX_STACKTRACE 1 26 27 #ifdef _GLIBCXX_SYSHDR 28 #pragma GCC system_header 29 #endif 30 31 #include <bits/requires_hosted.h> // std::string bound 32 33 #include <bits/c++config.h> 34 35 #define __glibcxx_want_stacktrace 36 #define __glibcxx_want_formatters 37 #include <bits/version.h> 38 39 #ifdef __cpp_lib_stacktrace // C++ >= 23 && hosted && HAVE_STACKTRACE 40 #include <compare> 41 #include <format> 42 #include <new> 43 #include <string> 44 #include <sstream> 45 #include <bits/memory_resource.h> 46 #include <bits/stl_algobase.h> 47 #include <bits/stl_algo.h> 48 #include <bits/stl_iterator.h> 49 #include <bits/stl_uninitialized.h> 50 #include <bits/stl_tempbuf.h> // __get_temporary_buffer 51 #include <ext/numeric_traits.h> 52 53 namespace std _GLIBCXX_VISIBILITY(default) 54 { 55 _GLIBCXX_BEGIN_NAMESPACE_VERSION 56 57 // [stacktrace.entry], class stacktrace_entry 58 class stacktrace_entry 59 { 60 using uint_least32_t = __UINT_LEAST32_TYPE__; 61 using uintptr_t = __UINTPTR_TYPE__; 62 63 public: 64 using native_handle_type = uintptr_t; 65 66 // [stacktrace.entry.ctor], constructors 67 68 constexpr 69 stacktrace_entry() noexcept = default; 70 71 constexpr 72 stacktrace_entry(const stacktrace_entry& __other) noexcept = default; 73 74 constexpr stacktrace_entry& 75 operator=(const stacktrace_entry& __other) noexcept = default; 76 77 ~stacktrace_entry() = default; 78 79 // [stacktrace.entry.obs], observers 80 81 [[nodiscard]] 82 constexpr native_handle_type 83 native_handle() const noexcept { return _M_pc; } 84 85 constexpr explicit operator bool() const noexcept 86 { return _M_pc != (native_handle_type)-1; } 87 88 // [stacktrace.entry.query], query 89 [[nodiscard]] 90 string 91 description() const 92 { 93 string __s; 94 _M_get_info(&__s, nullptr, nullptr); 95 return __s; 96 } 97 98 [[nodiscard]] 99 string 100 source_file() const 101 { 102 string __s; 103 _M_get_info(nullptr, &__s, nullptr); 104 return __s; 105 } 106 107 [[nodiscard]] 108 uint_least32_t 109 source_line() const 110 { 111 int __line = 0; 112 _M_get_info(nullptr, nullptr, &__line); 113 return __line; 114 } 115 116 // [stacktrace.entry.cmp], comparison 117 [[nodiscard]] 118 friend constexpr bool 119 operator==(const stacktrace_entry& __x, 120 const stacktrace_entry& __y) noexcept 121 { return __x._M_pc == __y._M_pc; } 122 123 [[nodiscard]] 124 friend constexpr strong_ordering 125 operator<=>(const stacktrace_entry& __x, 126 const stacktrace_entry& __y) noexcept 127 { return __x._M_pc <=> __y._M_pc; } 128 129 private: 130 native_handle_type _M_pc = -1; 131 132 template<typename _Allocator> friend class basic_stacktrace; 133 134 friend ostream& 135 operator<<(ostream&, const stacktrace_entry&); 136 137 // Type-erased wrapper for the fields of a stacktrace entry. 138 // This type is independent of which std::string ABI is in use. 139 struct _Info 140 { 141 void* _M_desc; 142 void* _M_file; 143 int* _M_line; 144 void (*_M_set)(void*, const char*); 145 146 _GLIBCXX_DEFAULT_ABI_TAG 147 static void 148 _S_set(void* __dest, const char* __str) 149 { static_cast<string*>(__dest)->assign(__str); } 150 151 _Info(string* __desc, string* __file, int* __line) 152 : _M_desc(__desc), _M_file(__file), _M_line(__line), _M_set(_S_set) 153 { } 154 155 bool 156 _M_populate(native_handle_type); 157 158 void _M_set_file(const char*); 159 void _M_set_desc(const char*); 160 }; 161 162 bool 163 _M_get_info(string* __desc, string* __file, int* __line) const 164 { 165 if (!*this) 166 return false; 167 return _Info(__desc, __file, __line)._M_populate(_M_pc); 168 } 169 }; 170 171 class __stacktrace_impl 172 { 173 protected: 174 static int _S_current(int (*) (void*, __UINTPTR_TYPE__), void*, int = 0); 175 }; 176 177 // [stacktrace.basic], class template basic_stacktrace 178 template<typename _Allocator> 179 class basic_stacktrace 180 : private __stacktrace_impl 181 { 182 using _AllocTraits = allocator_traits<_Allocator>; 183 using uintptr_t = __UINTPTR_TYPE__; 184 struct _Impl; 185 186 public: 187 using value_type = stacktrace_entry; 188 using const_reference = const value_type&; 189 using reference = value_type&; 190 using const_iterator 191 = __gnu_cxx::__normal_iterator<typename _AllocTraits::const_pointer, 192 _Impl>; 193 using iterator = const_iterator; 194 using reverse_iterator = std::reverse_iterator<iterator>; 195 using const_reverse_iterator = std::reverse_iterator<const_iterator>; 196 using difference_type = ptrdiff_t; 197 using size_type = unsigned short; 198 using allocator_type = _Allocator; 199 200 // [stacktrace.basic.ctor], creation and assignment 201 202 [[__gnu__::__noinline__]] 203 static basic_stacktrace 204 current(const allocator_type& __alloc = allocator_type()) noexcept 205 { 206 basic_stacktrace __ret(__alloc); 207 if (auto __cb = __ret._M_prepare()) [[likely]] 208 { 209 if (_S_current(__cb, std::__addressof(__ret))) 210 __ret._M_clear(); 211 } 212 return __ret; 213 } 214 215 [[__gnu__::__noinline__]] 216 static basic_stacktrace 217 current(size_type __skip, 218 const allocator_type& __alloc = allocator_type()) noexcept 219 { 220 basic_stacktrace __ret(__alloc); 221 if (__skip >= __INT_MAX__) [[unlikely]] 222 return __ret; 223 if (auto __cb = __ret._M_prepare()) [[likely]] 224 { 225 if (_S_current(__cb, std::__addressof(__ret), __skip)) 226 __ret._M_clear(); 227 } 228 229 return __ret; 230 } 231 232 [[__gnu__::__noinline__]] 233 static basic_stacktrace 234 current(size_type __skip, size_type __max_depth, 235 const allocator_type& __alloc = allocator_type()) noexcept 236 { 237 __glibcxx_assert(__skip <= (size_type(-1) - __max_depth)); 238 239 basic_stacktrace __ret(__alloc); 240 if (__max_depth == 0) [[unlikely]] 241 return __ret; 242 if (__skip >= __INT_MAX__) [[unlikely]] 243 return __ret; 244 if (auto __cb = __ret._M_prepare(__max_depth)) [[likely]] 245 { 246 if (_S_current(__cb, std::__addressof(__ret), __skip) < 0) 247 __ret._M_clear(); 248 else if (__ret.size() > __max_depth) 249 { 250 __ret._M_impl._M_resize(__max_depth, __ret._M_alloc); 251 252 if (__ret._M_impl._M_capacity / 2 >= __max_depth) 253 { 254 // shrink to fit 255 _Impl __tmp = __ret._M_impl._M_clone(__ret._M_alloc); 256 if (__tmp._M_capacity) 257 { 258 __ret._M_clear(); 259 __ret._M_impl = __tmp; 260 } 261 } 262 } 263 } 264 return __ret; 265 } 266 267 basic_stacktrace() 268 noexcept(is_nothrow_default_constructible_v<allocator_type>) 269 { } 270 271 explicit 272 basic_stacktrace(const allocator_type& __alloc) noexcept 273 : _M_alloc(__alloc) 274 { } 275 276 basic_stacktrace(const basic_stacktrace& __other) noexcept 277 : basic_stacktrace(__other, 278 _AllocTraits::select_on_container_copy_construction(__other._M_alloc)) 279 { } 280 281 basic_stacktrace(basic_stacktrace&& __other) noexcept 282 : _M_alloc(std::move(__other._M_alloc)), 283 _M_impl(std::__exchange(__other._M_impl, {})) 284 { } 285 286 basic_stacktrace(const basic_stacktrace& __other, 287 const allocator_type& __alloc) noexcept 288 : _M_alloc(__alloc) 289 { 290 if (const auto __s = __other._M_impl._M_size) 291 _M_impl = __other._M_impl._M_clone(_M_alloc); 292 } 293 294 basic_stacktrace(basic_stacktrace&& __other, 295 const allocator_type& __alloc) noexcept 296 : _M_alloc(__alloc) 297 { 298 if constexpr (_AllocTraits::is_always_equal::value) 299 _M_impl = std::__exchange(__other._M_impl, {}); 300 else if (_M_alloc == __other._M_alloc) 301 _M_impl = std::__exchange(__other._M_impl, {}); 302 else if (const auto __s = __other._M_impl._M_size) 303 _M_impl = __other._M_impl._M_clone(_M_alloc); 304 } 305 306 basic_stacktrace& 307 operator=(const basic_stacktrace& __other) noexcept 308 { 309 if (std::__addressof(__other) == this) 310 return *this; 311 312 constexpr bool __pocca 313 = _AllocTraits::propagate_on_container_copy_assignment::value; 314 constexpr bool __always_eq = _AllocTraits::is_always_equal::value; 315 316 const auto __s = __other.size(); 317 318 if constexpr (!__always_eq && __pocca) 319 { 320 if (_M_alloc != __other._M_alloc) 321 { 322 // Cannot keep the same storage, so deallocate it now. 323 _M_clear(); 324 } 325 } 326 327 if (_M_impl._M_capacity < __s) 328 { 329 // Need to allocate new storage. 330 _M_clear(); 331 332 if constexpr (__pocca) 333 _M_alloc = __other._M_alloc; 334 335 _M_impl = __other._M_impl._M_clone(_M_alloc); 336 } 337 else 338 { 339 // Current storage is large enough. 340 _M_impl._M_resize(0, _M_alloc); 341 _M_impl._M_assign(__other._M_impl, _M_alloc); 342 343 if constexpr (__pocca) 344 _M_alloc = __other._M_alloc; 345 } 346 347 return *this; 348 } 349 350 basic_stacktrace& 351 operator=(basic_stacktrace&& __other) noexcept 352 { 353 if (std::__addressof(__other) == this) 354 return *this; 355 356 constexpr bool __pocma 357 = _AllocTraits::propagate_on_container_move_assignment::value; 358 359 if constexpr (_AllocTraits::is_always_equal::value) 360 std::swap(_M_impl, __other._M_impl); 361 else if (_M_alloc == __other._M_alloc) 362 std::swap(_M_impl, __other._M_impl); 363 else if constexpr (__pocma) 364 { 365 // Free current storage and take ownership of __other's storage. 366 _M_clear(); 367 _M_impl = std::__exchange(__other._M_impl, {}); 368 } 369 else // Allocators are unequal and don't propagate. 370 { 371 const size_type __s = __other.size(); 372 373 if (_M_impl._M_capacity < __s) 374 { 375 // Need to allocate new storage. 376 _M_clear(); 377 _M_impl = __other._M_impl._M_clone(_M_alloc); 378 } 379 else 380 { 381 // Current storage is large enough. 382 _M_impl._M_resize(0, _M_alloc); 383 _M_impl._M_assign(__other._M_impl, _M_alloc); 384 } 385 } 386 387 if constexpr (__pocma) 388 _M_alloc = std::move(__other._M_alloc); 389 390 return *this; 391 } 392 393 constexpr ~basic_stacktrace() 394 { 395 _M_clear(); 396 } 397 398 // [stacktrace.basic.obs], observers 399 [[nodiscard]] 400 allocator_type get_allocator() const noexcept { return _M_alloc; } 401 402 [[nodiscard]] 403 const_iterator 404 begin() const noexcept 405 { return const_iterator{_M_impl._M_frames}; } 406 407 [[nodiscard]] 408 const_iterator 409 end() const noexcept 410 { return begin() + size(); } 411 412 [[nodiscard]] 413 const_reverse_iterator 414 rbegin() const noexcept 415 { return std::make_reverse_iterator(end()); } 416 417 [[nodiscard]] 418 const_reverse_iterator 419 rend() const noexcept 420 { return std::make_reverse_iterator(begin()); } 421 422 [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } 423 [[nodiscard]] const_iterator cend() const noexcept { return end(); } 424 425 [[nodiscard]] 426 const_reverse_iterator 427 crbegin() const noexcept { return rbegin(); }; 428 429 [[nodiscard]] 430 const_reverse_iterator 431 crend() const noexcept { return rend(); }; 432 433 [[nodiscard]] bool empty() const noexcept { return size() == 0; } 434 [[nodiscard]] size_type size() const noexcept { return _M_impl._M_size; } 435 436 [[nodiscard]] 437 size_type 438 max_size() const noexcept 439 { return _Impl::_S_max_size(_M_alloc); } 440 441 [[nodiscard]] 442 const_reference 443 operator[](size_type __n) const noexcept 444 { 445 __glibcxx_assert(__n < size()); 446 return begin()[__n]; 447 } 448 449 [[nodiscard]] 450 const_reference 451 at(size_type __n) const 452 { 453 if (__n >= size()) 454 __throw_out_of_range("basic_stacktrace::at: bad frame number"); 455 return begin()[__n]; 456 } 457 458 // [stacktrace.basic.cmp], comparisons 459 template<typename _Allocator2> 460 [[nodiscard]] 461 friend bool 462 operator==(const basic_stacktrace& __x, 463 const basic_stacktrace<_Allocator2>& __y) noexcept 464 { return std::equal(__x.begin(), __x.end(), __y.begin(), __y.end()); } 465 466 template<typename _Allocator2> 467 [[nodiscard]] 468 friend strong_ordering 469 operator<=>(const basic_stacktrace& __x, 470 const basic_stacktrace<_Allocator2>& __y) noexcept 471 { 472 if (auto __s = __x.size() <=> __y.size(); __s != 0) 473 return __s; 474 return std::lexicographical_compare_three_way(__x.begin(), __x.end(), 475 __y.begin(), __y.end()); 476 } 477 478 // [stacktrace.basic.mod], modifiers 479 void 480 swap(basic_stacktrace& __other) noexcept 481 { 482 std::swap(_M_impl, __other._M_impl); 483 if constexpr (_AllocTraits::propagate_on_container_swap::value) 484 std::swap(_M_alloc, __other._M_alloc); 485 else if constexpr (!_AllocTraits::is_always_equal::value) 486 { 487 __glibcxx_assert(_M_alloc == __other._M_alloc); 488 } 489 } 490 491 private: 492 bool 493 _M_push_back(const value_type& __x) noexcept 494 { 495 return _M_impl._M_push_back(_M_alloc, __x); 496 } 497 498 void 499 _M_clear() noexcept 500 { 501 _M_impl._M_resize(0, _M_alloc); 502 _M_impl._M_deallocate(_M_alloc); 503 } 504 505 // Precondition: __max_depth != 0 506 auto 507 _M_prepare(size_type __max_depth = -1) noexcept 508 -> int (*) (void*, uintptr_t) 509 { 510 auto __cb = +[](void* __data, uintptr_t __pc) { 511 auto& __s = *static_cast<basic_stacktrace*>(__data); 512 stacktrace_entry __f; 513 __f._M_pc = __pc; 514 if (__s._M_push_back(__f)) [[likely]] 515 return 0; // continue tracing 516 return -1; // stop tracing due to error 517 }; 518 519 if (__max_depth > 128) 520 __max_depth = 64; // soft limit, _M_push_back will reallocate 521 else 522 __cb = [](void* __data, uintptr_t __pc) { 523 auto& __s = *static_cast<basic_stacktrace*>(__data); 524 stacktrace_entry __f; 525 __f._M_pc = __pc; 526 if (__s.size() == __s._M_impl._M_capacity) [[unlikely]] 527 return 1; // stop tracing due to reaching max depth 528 if (__s._M_push_back(__f)) [[likely]] 529 return 0; // continue tracing 530 return -1; // stop tracing due to error 531 }; 532 533 if (_M_impl._M_allocate(_M_alloc, __max_depth)) [[likely]] 534 return __cb; 535 return nullptr; 536 } 537 538 struct _Impl 539 { 540 using pointer = typename _AllocTraits::pointer; 541 542 pointer _M_frames = nullptr; 543 size_type _M_size = 0; 544 size_type _M_capacity = 0; 545 546 static size_type 547 _S_max_size(const allocator_type& __alloc) noexcept 548 { 549 const size_t __size_max = __gnu_cxx::__int_traits<size_type>::__max; 550 const size_t __alloc_max = _AllocTraits::max_size(__alloc); 551 return std::min(__size_max, __alloc_max); 552 } 553 554 // Precondition: _M_frames == nullptr && __n != 0 555 pointer 556 _M_allocate(allocator_type& __alloc, size_type __n) noexcept 557 { 558 if (__n <= _S_max_size(__alloc)) [[likely]] 559 { 560 if constexpr (is_same_v<allocator_type, allocator<value_type>>) 561 { 562 // Use non-throwing __get_temporary_buffer, so that we 563 // don't need to handle exceptions from __alloc.allocate(n). 564 auto __p = __detail::__get_temporary_buffer<value_type>(__n); 565 if (__p == nullptr) [[unlikely]] 566 return nullptr; 567 _M_frames = __p; 568 } 569 else 570 { 571 __try 572 { 573 _M_frames = __alloc.allocate(__n); 574 } 575 __catch (...) 576 { 577 return nullptr; 578 } 579 } 580 _M_capacity = __n; 581 return _M_frames; 582 } 583 return nullptr; 584 } 585 586 void 587 _M_deallocate(allocator_type& __alloc) noexcept 588 { 589 if (_M_capacity) 590 { 591 if constexpr (is_same_v<allocator_type, allocator<value_type>>) 592 __detail::__return_temporary_buffer(_M_frames, _M_capacity); 593 else 594 __alloc.deallocate(_M_frames, _M_capacity); 595 _M_frames = nullptr; 596 _M_capacity = 0; 597 } 598 } 599 600 // Precondition: __n <= _M_size 601 void 602 _M_resize(size_type __n, allocator_type& __alloc) noexcept 603 { 604 std::_Destroy(_M_frames + __n, _M_frames + _M_size, __alloc); 605 _M_size = __n; 606 } 607 608 bool 609 _M_push_back(allocator_type& __alloc, 610 const stacktrace_entry& __f) noexcept 611 { 612 if (_M_size == _M_capacity) [[unlikely]] 613 { 614 _Impl __tmp = _M_xclone(_M_capacity ? _M_capacity : 8, __alloc); 615 if (!__tmp._M_capacity) [[unlikely]] 616 return false; 617 _M_resize(0, __alloc); 618 _M_deallocate(__alloc); 619 *this = __tmp; 620 } 621 stacktrace_entry* __addr = std::to_address(_M_frames + _M_size++); 622 _AllocTraits::construct(__alloc, __addr, __f); 623 return true; 624 } 625 626 // Precondition: _M_size != 0 627 _Impl 628 _M_clone(allocator_type& __alloc) const noexcept 629 { 630 return _M_xclone(_M_size, __alloc); 631 } 632 633 // Precondition: _M_size != 0 || __extra != 0 634 _Impl 635 _M_xclone(size_type __extra, allocator_type& __alloc) const noexcept 636 { 637 _Impl __i; 638 if (__i._M_allocate(__alloc, _M_size + __extra)) [[likely]] 639 __i._M_assign(*this, __alloc); 640 return __i; 641 } 642 643 // Precondition: _M_capacity >= __other._M_size 644 void 645 _M_assign(const _Impl& __other, allocator_type& __alloc) noexcept 646 { 647 std::__uninitialized_copy_a(__other._M_frames, 648 __other._M_frames + __other._M_size, 649 _M_frames, __alloc); 650 _M_size = __other._M_size; 651 } 652 }; 653 654 [[no_unique_address]] allocator_type _M_alloc{}; 655 656 _Impl _M_impl{}; 657 }; 658 659 // basic_stacktrace typedef names 660 using stacktrace = basic_stacktrace<allocator<stacktrace_entry>>; 661 662 // [stacktrace.basic.nonmem], non-member functions 663 template<typename _Allocator> 664 inline void 665 swap(basic_stacktrace<_Allocator>& __a, basic_stacktrace<_Allocator>& __b) 666 noexcept(noexcept(__a.swap(__b))) 667 { __a.swap(__b); } 668 669 inline ostream& 670 operator<<(ostream& __os, const stacktrace_entry& __f) 671 { 672 string __desc, __file; 673 int __line; 674 if (__f._M_get_info(&__desc, &__file, &__line)) 675 { 676 __os.width(4); 677 __os << __desc << " at " << __file << ':' << __line; 678 } 679 return __os; 680 } 681 682 template<typename _Allocator> 683 inline ostream& 684 operator<<(ostream& __os, const basic_stacktrace<_Allocator>& __st) 685 { 686 for (stacktrace::size_type __i = 0; __i < __st.size(); ++__i) 687 { 688 __os.width(4); 689 __os << __i << "# " << __st[__i] << '\n'; 690 } 691 return __os; 692 } 693 694 [[nodiscard]] 695 inline string 696 to_string(const stacktrace_entry& __f) 697 { 698 std::ostringstream __os; 699 __os << __f; 700 return std::move(__os).str(); 701 } 702 703 template<typename _Allocator> 704 [[nodiscard]] 705 string 706 to_string(const basic_stacktrace<_Allocator>& __st) 707 { 708 std::ostringstream __os; 709 __os << __st; 710 return std::move(__os).str(); 711 } 712 713 template<> 714 class formatter<stacktrace_entry> 715 { 716 public: 717 constexpr typename basic_format_parse_context<char>::iterator 718 parse(basic_format_parse_context<char>& __pc) 719 { 720 __format::_Spec<char> __spec{}; 721 const auto __last = __pc.end(); 722 auto __first = __pc.begin(); 723 724 auto __finalize = [this, &__spec] { 725 _M_spec = __spec; 726 }; 727 728 auto __finished = [&] { 729 if (__first == __last || *__first == '}') 730 { 731 __finalize(); 732 return true; 733 } 734 return false; 735 }; 736 737 if (__finished()) 738 return __first; 739 740 __first = __spec._M_parse_fill_and_align(__first, __last); 741 if (__finished()) 742 return __first; 743 744 __first = __spec._M_parse_width(__first, __last, __pc); 745 if (__finished()) 746 return __first; 747 748 __throw_format_error("format error: invalid format-spec for " 749 "std::stacktrace_entry"); 750 } 751 752 template<typename _Out> 753 typename basic_format_context<_Out, char>::iterator 754 format(const stacktrace_entry& __x, 755 basic_format_context<_Out, char>& __fc) const 756 { 757 std::ostringstream __os; 758 __os << __x; 759 auto __str = __os.view(); 760 return __format::__write_padded_as_spec(__str, __str.size(), 761 __fc, _M_spec); 762 } 763 764 private: 765 __format::_Spec<char> _M_spec; 766 }; 767 768 template<typename _Allocator> 769 class formatter<basic_stacktrace<_Allocator>> 770 { 771 public: 772 constexpr typename basic_format_parse_context<char>::iterator 773 parse(basic_format_parse_context<char>& __pc) 774 { 775 const auto __first = __pc.begin(); 776 if (__first == __pc.end() || *__first == '}') 777 return __first; 778 __throw_format_error("format error: invalid format-spec for " 779 "std::basic_stacktrace"); 780 } 781 782 template<typename _Out> 783 typename basic_format_context<_Out, char>::iterator 784 format(const basic_stacktrace<_Allocator>& __x, 785 basic_format_context<_Out, char>& __fc) const 786 { 787 std::ostringstream __os; 788 __os << __x; 789 return __format::__write(__fc.out(), __os.view()); 790 } 791 }; 792 793 namespace pmr 794 { 795 using stacktrace 796 = basic_stacktrace<polymorphic_allocator<stacktrace_entry>>; 797 } 798 799 // [stacktrace.basic.hash], hash support 800 801 template<> 802 struct hash<stacktrace_entry> 803 { 804 [[nodiscard]] 805 size_t 806 operator()(const stacktrace_entry& __f) const noexcept 807 { 808 using __h = hash<stacktrace_entry::native_handle_type>; 809 return __h()(__f.native_handle()); 810 } 811 }; 812 813 template<typename _Allocator> 814 struct hash<basic_stacktrace<_Allocator>> 815 { 816 [[nodiscard]] 817 size_t 818 operator()(const basic_stacktrace<_Allocator>& __st) const noexcept 819 { 820 hash<stacktrace_entry> __h; 821 size_t __val = _Hash_impl::hash(__st.size()); 822 for (const auto& __f : __st) 823 __val = _Hash_impl::__hash_combine(__h(__f), __val); 824 return __val; 825 } 826 }; 827 828 _GLIBCXX_END_NAMESPACE_VERSION 829 } // namespace std 830 #endif // __cpp_lib_stacktrace 831 #endif /* _GLIBCXX_STACKTRACE */