diff --git a/source/lib-intro.tex b/source/lib-intro.tex index c34167ea33..f4217f5686 100644 --- a/source/lib-intro.tex +++ b/source/lib-intro.tex @@ -982,10 +982,11 @@ \tcode{} \\ \tcode{} \\ \tcode{} \\ +\tcode{} \\ \tcode{} \\ \tcode{} \\ -\tcode{} \\ \columnbreak +\tcode{} \\ \tcode{} \\ \tcode{} \\ \tcode{} \\ diff --git a/source/support.tex b/source/support.tex index 5fcba979c4..50132b726e 100644 --- a/source/support.tex +++ b/source/support.tex @@ -672,6 +672,7 @@ #define @\defnlibxname{cpp_lib_source_location}@ 201907L // also in \libheader{source_location} #define @\defnlibxname{cpp_lib_span}@ 202002L // also in \libheader{span} #define @\defnlibxname{cpp_lib_ssize}@ 201902L // also in \libheader{iterator} +#define @\defnlibxname{cpp_lib_stacktrace}@ 202011L // also in \libheader{stacktrace} #define @\defnlibxname{cpp_lib_starts_ends_with}@ 201711L // also in \libheader{string}, \libheader{string_view} #define @\defnlibxname{cpp_lib_string_contains}@ 202011L // also in \libheader{string}, \libheader{string_view} #define @\defnlibxname{cpp_lib_string_udls}@ 201304L // also in \libheader{string} diff --git a/source/utilities.tex b/source/utilities.tex index 6da0fda55a..7c966198da 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -21111,3 +21111,718 @@ \ensures \tcode{strcmp(what(), what_arg) == 0}. \end{itemdescr} + +\rSec1[stacktrace]{Stacktrace} + +\rSec2[stacktrace.general]{General} + +\pnum +Subclause \ref{stacktrace} describes components +that \Cpp{} programs may use to store +the stacktrace of the current thread of execution and +query information about the stored stacktrace at runtime. + +\pnum +The \defn{invocation sequence} of the current evaluation $x_0$ +in the current thread of execution +is a sequence $(x_0, \ldots, x_n)$ of evaluations such that, for $i \geq 0$, +$x_i$ is within the function invocation $x_{i+1}$\iref{intro.execution}. + +\pnum +A \defn{stacktrace} is an approximate representation of +an invocation sequence and consists of stacktrace entries. +A \defn{stacktrace entry} represents an evaluation in a stacktrace. + +\rSec2[stacktrace.syn]{Header \tcode{} synopsis} + +\begin{codeblock} +namespace std { + // \ref{stacktrace.entry}, class \tcode{stacktrace_entry} + class stacktrace_entry; + + // \ref{stacktrace.basic}, class template \tcode{basic_stacktrace} + template + class basic_stacktrace; + + // \tcode{basic_stacktrace} typedef names + using stacktrace = basic_stacktrace>; + + // \ref{stacktrace.basic.nonmem}, non-member functions + template + void swap(basic_stacktrace& a, basic_stacktrace& b) + noexcept(noexcept(a.swap(b))); + + string to_string(const stacktrace_entry& f); + + template + string to_string(const basic_stacktrace& st); + + template + basic_ostream& + operator<<(basic_ostream& os, const stacktrace_entry& f); + + template + basic_ostream& + operator<<(basic_ostream& os, const basic_stacktrace& st); + + // \ref{stacktrace.basic.hash}, hash support + template struct hash; + template<> struct hash; + template struct hash>; +} +\end{codeblock} + +\rSec2[stacktrace.entry]{Class \tcode{stacktrace_entry}} + +\rSec3[stacktrace.entry.overview]{Overview} + +\begin{codeblock} +namespace std { + class @\libglobal{stacktrace_entry}@ { + public: + using native_handle_type = @\impldef{\tcode{stacktrace_entry::native_handle_type}}@; + + // \ref{stacktrace.entry.ctor}, constructors + constexpr stacktrace_entry() noexcept; + constexpr stacktrace_entry(const stacktrace_entry& other) noexcept; + constexpr stacktrace_entry& operator=(const stacktrace_entry& other) noexcept; + + ~stacktrace_entry(); + + // \ref{stacktrace.entry.obs}, observers + constexpr native_handle_type native_handle() const noexcept; + constexpr explicit operator bool() const noexcept; + + // \ref{stacktrace.entry.query}, query + string description() const; + string source_file() const; + uint_least32_t source_line() const; + + // \ref{stacktrace.entry.cmp}, comparison + friend constexpr bool operator==(const stacktrace_entry& x, + const stacktrace_entry& y) noexcept; + friend constexpr strong_ordering operator<=>(const stacktrace_entry& x, + const stacktrace_entry& y) noexcept; + }; +} +\end{codeblock} + +\pnum +An object of type \tcode{stacktrace_entry} is either empty, +or represents a stacktrace entry and +provides operations for querying information about it. +The class \tcode{stacktrace_entry} models +\libconcept{regular}\iref{concepts.object} and +\tcode{\libconcept{three_way_comparable}}\iref{cmp.concept}. + +\rSec3[stacktrace.entry.ctor]{Constructors} + +\indexlibraryctor{stacktrace_entry}% +\begin{itemdecl} +constexpr stacktrace_entry() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{*this} is empty. +\end{itemdescr} + +\rSec3[stacktrace.entry.obs]{Observers} + +\indexlibrarymember{native_handle}{stacktrace_entry}% +\begin{itemdecl} +constexpr native_handle_type native_handle() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The semantics of this function are +\impldef{semantics of \tcode{stacktrace_entry::native_handle}}. + +\pnum +\remarks +Successive invocations of the \tcode{native_handle} function +for an unchanged \tcode{stacktrace_entry} object return identical values. +\end{itemdescr} + +\indexlibrarymember{operator bool}{stacktrace_entry}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{false} if and only if \tcode{*this} is empty. +\end{itemdescr} + +\rSec3[stacktrace.entry.query]{Query} + +\pnum +\begin{note} +All the \tcode{stacktrace_entry} query functions treat +errors other than memory allocation errors +as ``no information available'' and do not throw in that case. +\end{note} + +\indexlibrarymember{description}{stacktrace_entry}% +\begin{itemdecl} +string description() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +A description of the evaluation represented by \tcode{*this}, +or an empty string. + +\pnum +\throws +\tcode{bad_alloc} if memory for +the internal data structures or the resulting string cannot be allocated. +\end{itemdescr} + +\indexlibrarymember{source_file}{stacktrace_entry}% +\begin{itemdecl} +string source_file() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +The presumed or actual name of the source file\iref{cpp.predefined} +that lexically contains the expression or statement +whose evaluation is represented by \tcode{*this}, or an empty string. + +\pnum +\throws +\tcode{bad_alloc} if memory for +the internal data structures or the resulting string cannot be allocated. +\end{itemdescr} + +\indexlibrarymember{source_line}{stacktrace_entry}% +\begin{itemdecl} +uint_least32_t source_line() const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{0}, or a 1-based line number that lexically relates to the evaluation +represented by \tcode{*this}. +If \tcode{source_file} returns the presumed name of the source file, +returns the presumed line number; +if \tcode{source_file} returns the actual name of the source file, +returns the actual line number. + +\pnum +\throws +\tcode{bad_alloc} if memory for +the internal data structures or the resulting string cannot be allocated. +\end{itemdescr} + +\rSec3[stacktrace.entry.cmp]{Comparison} + +\indexlibrarymember{operator==}{stacktrace_entry}% +\begin{itemdecl} +friend constexpr bool operator==(const stacktrace_entry& x, const stacktrace_entry& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{true} if and only if \tcode{x} and \tcode{y} represent +the same stacktrace entry or both \tcode{x} and \tcode{y} are empty. +\end{itemdescr} + +\rSec2[stacktrace.basic]{Class template \tcode{basic_stacktrace}} + +\rSec3[stacktrace.basic.overview]{Overview} + +\begin{codeblock} + template + class @\libglobal{basic_stacktrace}@ { + public: + using value_type = stacktrace_entry; + using const_reference = const value_type&; + using reference = value_type&; + using const_iterator = @\impdefx{type of \tcode{basic_stacktrace::const_iterator}}@; // see \ref{stacktrace.basic.obs} + using iterator = const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = @\impdefx{type of \tcode{basic_stacktrace::difference_type}}@; + using size_type = @\impdefx{type of \tcode{basic_stacktrace::size_type}}@; + using allocator_type = Allocator; + + // \ref{stacktrace.basic.ctor}, creation and assignment + static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; + static basic_stacktrace current(size_type skip, + const allocator_type& alloc = allocator_type()) noexcept; + static basic_stacktrace current(size_type skip, size_type max_depth, + const allocator_type& alloc = allocator_type()) noexcept; + + basic_stacktrace() noexcept(is_nothrow_default_constructible_v); + explicit basic_stacktrace(const allocator_type& alloc) noexcept; + + basic_stacktrace(const basic_stacktrace& other); + basic_stacktrace(basic_stacktrace&& other) noexcept; + basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); + basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); + basic_stacktrace& operator=(const basic_stacktrace& other); + basic_stacktrace& operator=(basic_stacktrace&& other) + noexcept(allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value); + + ~basic_stacktrace(); + + // \ref{stacktrace.basic.obs}, observers + allocator_type get_allocator() const noexcept; + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator rend() const noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + + [[nodiscard]] bool empty() const noexcept; + size_type size() const noexcept; + size_type max_size() const noexcept; + + const_reference operator[](size_type) const; + const_reference at(size_type) const; + + // \ref{stacktrace.basic.cmp}, comparisons + template + friend bool operator==(const basic_stacktrace& x, + const basic_stacktrace& y) noexcept; + template + friend strong_ordering operator<=>(const basic_stacktrace& x, + const basic_stacktrace& y) noexcept; + + // \ref{stacktrace.basic.mod}, modifiers + void swap(basic_stacktrace& other) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); + + private: + vector frames_; // \expos + }; +} +\end{codeblock} + +\pnum +The class template \tcode{basic_stacktrace} satisfies +the requirements of +an allocator-aware container (\tref{container.alloc.req}), +a sequence container\iref{sequence.reqmts}, and +a reversible container\iref{container.requirements.general} +except that +\begin{itemize} +\item +only move, assignment, swap, and +operations defined for const-qualified sequence containers are supported and, +\item +the semantics of comparison functions +are different from those required for a container. +\end{itemize} + +\rSec3[stacktrace.basic.ctor]{Creation and assignment} + +\indexlibrarymember{current}{basic_stacktrace}% +\begin{itemdecl} +static basic_stacktrace current(const allocator_type& alloc = allocator_type()) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +A \tcode{basic_stacktrace} object +with \tcode{frames_} storing +the stacktrace of the current evaluation in the current thread of execution, or +an empty \tcode{basic_stacktrace} object +if the initialization of \tcode{frames_} failed. +\tcode{alloc} is passed to the constructor of the \tcode{frames_} object. + +\begin{note} +If the stacktrace was successfully obtained, +then \tcode{frames_.front()} is the \tcode{stacktrace_entry} +representing approximately the current evaluation, and +\tcode{frames_.back()} is the \tcode{stacktrace_entry} +representing approximately the initial function of +the current thread of execution. +\end{note} +\end{itemdescr} + +\indexlibrarymember{current}{basic_stacktrace}% +\begin{itemdecl} +static basic_stacktrace current(size_type skip, + const allocator_type& alloc = allocator_type()) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{t} be a stacktrace +as-if obtained via \tcode{basic_stacktrace::current(alloc)}. +Let \tcode{n} be \tcode{t.size()}. + +\pnum +\returns +A \tcode{basic_stacktrace} object +where \tcode{frames_} is direct-non-list-initialized from arguments +\tcode{t.begin() + min(n, skip)}, \tcode{t.end()}, and \tcode{alloc}, +or an empty \tcode{basic_stacktrace} object +if the initialization of \tcode{frames_} failed. +\end{itemdescr} + +\indexlibrarymember{current}{basic_stacktrace}% +\begin{itemdecl} +static basic_stacktrace current(size_type skip, size_type max_depth, + const allocator_type& alloc = allocator_type()) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{t} be a stacktrace +as-if obtained via \tcode{basic_stacktrace::current(alloc)}. +Let \tcode{n} be \tcode{t.size()}. + +\pnum +\expects +\tcode{skip <= skip + max_depth} is \tcode{true}. + +\pnum +\returns +A \tcode{basic_stacktrace} object +where \tcode{frames_} is direct-non-list-initialized from arguments +\tcode{t.begin() + min(n, skip)}, \tcode{t.begin() + min(n, skip + max_depth)}, +and \tcode{alloc}, +or an empty \tcode{basic_stacktrace} object +if the initialization of \tcode{frames_} failed. +\end{itemdescr} + +\indexlibraryctor{basic_stacktrace}% +\begin{itemdecl} +basic_stacktrace() noexcept(is_nothrow_default_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{empty()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{basic_stacktrace}% +\begin{itemdecl} +explicit basic_stacktrace(const allocator_type& alloc) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\tcode{alloc} is passed to the \tcode{frames_} constructor. + +\pnum +\ensures +\tcode{empty()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{basic_stacktrace}% +\indexlibrarymember{operator=}{basic_stacktrace}% +\begin{itemdecl} +basic_stacktrace(const basic_stacktrace& other); +basic_stacktrace(const basic_stacktrace& other, const allocator_type& alloc); +basic_stacktrace(basic_stacktrace&& other, const allocator_type& alloc); +basic_stacktrace& operator=(const basic_stacktrace& other); +basic_stacktrace& operator=(basic_stacktrace&& other) + noexcept(allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +Implementations may strengthen the exception specification +for these functions\iref{res.on.exception.handling} +by ensuring that \tcode{empty()} is \tcode{true} on failed allocation. +\end{itemdescr} + +\rSec3[stacktrace.basic.obs]{Observers} + +\indexlibrarymember{const_iterator}{basic_stacktrace}% +\begin{itemdecl} +using const_iterator = @\impdef@; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The type models +\libconcept{random_access_iterator}\iref{iterator.concept.random.access} and +meets the +\oldconcept{RandomAccessIterator} requirements\iref{random.access.iterators}. +\end{itemdescr} + +\indexlibrarymember{get_allocator}{basic_stacktrace}% +\begin{itemdecl} +allocator_type get_allocator() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{frames_.get_allocator()}. +\end{itemdescr} + +\indexlibrarymember{begin}{basic_stacktrace}% +\indexlibrarymember{cbegin}{basic_stacktrace}% +\begin{itemdecl} +const_iterator begin() const noexcept; +const_iterator cbegin() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An iterator referring to the first element in \tcode{frames_}. +If \tcode{empty()} is \tcode{true}, +then it returns the same value as \tcode{end()}. +\end{itemdescr} + +\indexlibrarymember{end}{basic_stacktrace}% +\indexlibrarymember{cend}{basic_stacktrace}% +\begin{itemdecl} +const_iterator end() const noexcept; +const_iterator cend() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +The end iterator. +\end{itemdescr} + +\indexlibrarymember{rbegin}{basic_stacktrace}% +\indexlibrarymember{crbegin}{basic_stacktrace}% +\begin{itemdecl} +const_reverse_iterator rbegin() const noexcept; +const_reverse_iterator crbegin() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{reverse_iterator(cend())}. +\end{itemdescr} + +\indexlibrarymember{rend}{basic_stacktrace}% +\indexlibrarymember{crend}{basic_stacktrace}% +\begin{itemdecl} +const_reverse_iterator rend() const noexcept; +const_reverse_iterator crend() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{reverse_iterator(cbegin())}. +\end{itemdescr} + +\indexlibrarymember{empty}{basic_stacktrace}% +\begin{itemdecl} +[[nodiscard]] bool empty() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{frames_.empty()}. +\end{itemdescr} + +\indexlibrarymember{size}{basic_stacktrace}% +\begin{itemdecl} +size_type size() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{frames_.size()}. +\end{itemdescr} + +\indexlibrarymember{max_size}{basic_stacktrace}% +\begin{itemdecl} +size_type max_size() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{frames_.max_size()}. +\end{itemdescr} + +\indexlibrarymember{operator[]}{basic_stacktrace}% +\begin{itemdecl} +const_reference operator[](size_type frame_no) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{frame_no < size()} is \tcode{true}. + +\pnum +\returns +\tcode{frames_[frame_no]}. + +\pnum +\throws +Nothing. +\end{itemdescr} + +\indexlibrarymember{at}{basic_stacktrace}% +\begin{itemdecl} +const_reference at(size_type frame_no) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{frames_[frame_no]}. + +\pnum +\throws +\tcode{out_of_range} if \tcode{frame_no >= size()}. +\end{itemdescr} + +\rSec3[stacktrace.basic.cmp]{Comparisons} + +\indexlibrarymember{operator==}{basic_stacktrace}% +\begin{itemdecl} +template +friend bool operator==(const basic_stacktrace& x, const basic_stacktrace& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{equal(x.begin(), x.end(), y.begin(), y.end())}. +\end{itemdescr} + +\indexlibrarymember{operator<=>}{basic_stacktrace}% +\begin{itemdecl} +template +friend strong_ordering + operator<=>(const basic_stacktrace& x, const basic_stacktrace& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{x.size() <=> y.size()} if \tcode{x.size() != y.size()}; +\tcode{lexicographical_compare_3way(\newline +x.begin(), x.end(), y.begin(), y.end())} +otherwise. +\end{itemdescr} + +\rSec3[stacktrace.basic.mod]{Modifiers} + +\indexlibrarymember{swap}{basic_stacktrace}% +\begin{itemdecl} +void swap(basic_stacktrace& other) + noexcept(allocator_traits::propagate_on_container_swap::value || + allocator_traits::is_always_equal::value); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges the contents of \tcode{*this} and \tcode{other}. +\end{itemdescr} + +\rSec3[stacktrace.basic.nonmem]{Non-member functions} + +\indexlibrarymember{swap}{basic_stacktrace}% +\begin{itemdecl} +template +void swap(basic_stacktrace& a, basic_stacktrace& b) + noexcept(noexcept(a.swap(b))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{a.swap(b)}. +\end{itemdescr} + +\indexlibrarymember{to_string}{basic_stacktrace}% +\begin{itemdecl} +string to_string(const stacktrace_entry& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +A string with a description of \tcode{f}. + +\recommended +The description should provide information about the contained evaluation, +including information from +\tcode{f.source_file()} and \tcode{f.source_line()}. +\end{itemdescr} + +\indexlibrarymember{to_string}{basic_stacktrace}% +\begin{itemdecl} +template +string to_string(const basic_stacktrace& st); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +A string with a description of \tcode{st}. +\begin{note} +The number of lines is not guaranteed to be equal to \tcode{st.size()}. +\end{note} +\end{itemdescr} + +\indexlibrarymember{operator<<}{stacktrace_entry}% +\begin{itemdecl} +template +basic_ostream& + operator<<(basic_ostream& os, const stacktrace_entry& f); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return os << to_string(f);} +\end{itemdescr} + +\indexlibrarymember{operator<<}{basic_stacktrace}% +\begin{itemdecl} +template +basic_ostream& + operator<<(basic_ostream& os, const basic_stacktrace& st); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: \tcode{return os << to_string(st);} +\end{itemdescr} + +\rSec3[stacktrace.basic.hash]{Hash support} + +\begin{itemdecl} +template<> struct hash; +template struct hash>; +\end{itemdecl} + +\begin{itemdescr} +\pnum +The specializations are enabled\iref{unord.hash}. +\end{itemdescr}