summaryrefslogtreecommitdiffhomepage
path: root/CompilerDriver/cc2/source/common.h
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2023-12-30 23:39:37 +0100
committerAmlal El Mahrouss <amlal.elmahrouss@icloud.com>2023-12-30 23:39:37 +0100
commit263915832993dd12beee10e204f9ebcc6c786ed2 (patch)
tree862e51208a99c35746e574a76564a4532b3a4a49 /CompilerDriver/cc2/source/common.h
Meta: initial commit of WestCo optimized toolchain.
Signed-off-by: Amlal El Mahrouss <amlal.elmahrouss@icloud.com>
Diffstat (limited to 'CompilerDriver/cc2/source/common.h')
-rw-r--r--CompilerDriver/cc2/source/common.h968
1 files changed, 968 insertions, 0 deletions
diff --git a/CompilerDriver/cc2/source/common.h b/CompilerDriver/cc2/source/common.h
new file mode 100644
index 0000000..fe0301c
--- /dev/null
+++ b/CompilerDriver/cc2/source/common.h
@@ -0,0 +1,968 @@
+
+// Copyright (c) Herb Sutter
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4456)
+#endif
+
+#include "cpp2util.h"
+
+
+//===========================================================================
+// Common types
+//===========================================================================
+
+#ifndef CPP2_COMMON_H
+#define CPP2_COMMON_H
+
+#include <string>
+#include <string_view>
+#include <vector>
+#include <cstdint>
+#include <cctype>
+#include <cassert>
+#include <iomanip>
+#include <compare>
+#include <algorithm>
+#include <unordered_map>
+
+namespace cpp2 {
+
+//-----------------------------------------------------------------------
+//
+// source_line: represents a source code line
+//
+//-----------------------------------------------------------------------
+//
+struct source_line
+{
+ std::string text;
+
+ enum class category { empty, preprocessor, comment, import, cpp1, cpp2, rawstring };
+ category cat;
+
+ bool all_tokens_are_densely_spaced = true; // to be overridden in lexing if they're not
+
+ source_line(
+ std::string_view t = {},
+ category c = category::empty
+ )
+ : text{t}
+ , cat{c}
+ { }
+
+ auto indent() const
+ -> int
+ {
+ return
+ std::find_if_not( text.begin(), text.end(), &isspace )
+ - text.begin();
+ }
+
+ auto prefix() const
+ -> std::string
+ {
+ switch (cat) {
+ break;case category::empty: return "/* */ ";
+ break;case category::preprocessor: return "/* # */ ";
+ break;case category::comment: return "/* / */ ";
+ break;case category::import: return "/* i */ ";
+ break;case category::cpp1: return "/* 1 */ ";
+ break;case category::cpp2: return "/* 2 */ ";
+ break;case category::rawstring: return "/* R */ ";
+ break;default: assert(!"illegal category"); abort();
+ }
+ }
+};
+
+
+using lineno_t = int32_t;
+using colno_t = int32_t; // not int16_t... encountered >80,000 char line during testing
+
+struct source_position
+{
+ lineno_t lineno; // one-based offset into program source
+ colno_t colno; // one-based offset into line
+
+ source_position(lineno_t l = 1, colno_t c = 1 )
+ : lineno{ l }, colno{ c }
+ {
+ }
+
+ auto operator<=>(source_position const&) const = default;
+
+ auto to_string() const
+ -> std::string
+ {
+ return "(" + std::to_string(lineno) + "," + std::to_string(colno) + ")";
+ }
+};
+
+struct comment
+{
+ enum class comment_kind { line_comment = 0, stream_comment };
+
+ comment_kind kind;
+ source_position start;
+ source_position end;
+ std::string text;
+
+ mutable bool dbg_was_printed = false;
+};
+
+struct string_parts {
+ struct cpp_code { std::string text; };
+ struct raw_string { std::string text; };
+ enum adds_sequences { no_ends = 0, on_the_beginning = 1, on_the_end = 2, on_both_ends = 3 };
+
+ string_parts(const std::string& beginseq,
+ const std::string& endseq,
+ adds_sequences strateg)
+ : begin_seq{beginseq}
+ , end_seq{endseq}
+ , strategy{strateg}
+ {
+ if (!(strategy & on_the_beginning)) {
+ parts.push_back(raw_string{""});
+ }
+ }
+
+ void add_code(const std::string& text) { parts.push_back(cpp_code{text});}
+ void add_string(const std::string& text) { parts.push_back(raw_string{text});}
+ void add_string(const std::string_view& text) { parts.push_back(raw_string{std::string(text)});}
+
+ void clear() { parts.clear(); }
+
+ auto generate() const -> std::string {
+
+ if (parts.empty()) {
+ return (strategy & on_the_beginning ? begin_seq : std::string{})
+ + (strategy & on_the_end ? end_seq : std::string{});
+ }
+
+ auto result = std::visit(begin_visit{begin_seq, strategy},
+ parts.front());
+
+ if (std::ssize(parts) > 1) {
+ auto it1 = parts.cbegin();
+ auto it2 = parts.cbegin()+1;
+ for(;it2 != parts.cend(); ++it1, ++it2) {
+ result += std::visit(generator_visit{begin_seq, end_seq}, *it1, *it2);
+ }
+ }
+
+ if (!(strategy & on_the_end)) {
+ result += std::visit([this](const auto& lhs) {
+ return generator_visit{begin_seq, end_seq}(lhs, raw_string{""});
+ }, parts.back());
+ }
+
+ result += std::visit(end_visit{end_seq, strategy}, parts.back());
+
+ return result;
+ }
+
+ auto is_expanded() const -> bool {
+ for (const auto& p : parts) {
+ if (std::holds_alternative<cpp_code>(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+private:
+ std::string begin_seq;
+ std::string end_seq;
+ adds_sequences strategy;
+ std::vector<std::variant<raw_string, cpp_code>> parts;
+
+ struct begin_visit {
+ std::string begin_seq;
+ adds_sequences strategy;
+
+ auto operator()(const raw_string& part) const -> std::string {
+ return (strategy & on_the_beginning ? begin_seq : "") + part.text;
+ }
+ auto operator()(const cpp_code& part) const -> std::string {
+ return part.text;
+ }
+ };
+
+ struct end_visit {
+ std::string end_seq;
+ adds_sequences strategy;
+ auto operator()(const raw_string&) const -> std::string {
+ return strategy & on_the_end ? end_seq : "";
+ }
+ auto operator()(const cpp_code&) const -> std::string {
+ return {};
+ }
+ };
+
+ struct generator_visit {
+ std::string begin_seq;
+ std::string end_seq;
+
+ auto operator()(const raw_string&, const cpp_code& part ) const -> std::string {
+ return end_seq + " + " + part.text;
+ }
+ auto operator()(const cpp_code&, const raw_string& part ) const -> std::string {
+ return " + " + begin_seq + part.text;
+ }
+ auto operator()(const raw_string&, const raw_string& part ) const -> std::string {
+ return part.text;
+ }
+ auto operator()(const cpp_code&, const cpp_code& part ) const -> std::string {
+ return " + " + part.text;
+ }
+ };
+};
+
+struct raw_string
+{
+ source_position start;
+ std::string text;
+ std::string opening_seq;
+ std::string closing_seq;
+ bool should_interpolate = false;
+};
+
+struct multiline_raw_string
+{
+ std::string text;
+ source_position end = {0, 0};
+};
+
+//-----------------------------------------------------------------------
+//
+// error: represents a user-readable error message
+//
+//-----------------------------------------------------------------------
+//
+struct error_entry
+{
+ source_position where;
+ std::string msg;
+ bool internal = false;
+ bool fallback = false; // only emit this message if there was nothing better
+
+ error_entry(
+ source_position w,
+ std::string_view m,
+ bool i = false,
+ bool f = false
+ )
+ : where{w}
+ , msg{m}
+ , internal{i}
+ , fallback{f}
+ { }
+
+ auto operator==(error_entry const& that)
+ -> bool
+ {
+ return
+ where == that.where
+ && msg == that.msg
+ ;
+ }
+
+ auto print(auto& o, std::string const& file) const
+ -> void;
+};
+
+
+//-----------------------------------------------------------------------
+//
+// Digit classification, with '\'' digit separators
+//
+//-----------------------------------------------------------------------
+//
+
+//G binary-digit:
+//G one of '0' '1'
+//G
+auto is_binary_digit(char c)
+ -> bool
+{
+ return
+ c == '0'
+ || c == '1'
+ ;
+}
+
+//G digit: one of
+//G binary-digit
+//G one of '2' '3' '4' '5' '6' '7' '8' '9'
+//G
+auto is_digit(char c)
+ -> bool
+{
+ return isdigit(c);
+}
+
+//G hexadecimal-digit:
+//G digit
+//G one of 'A' 'B' 'C' 'D' 'E' 'F'
+//G
+auto is_hexadecimal_digit(char c)
+ -> bool
+{
+ return isxdigit(c);
+}
+
+//G nondigit:
+//G one of 'a'..'z'
+//G one of 'A'..'Z'
+//G _
+//G
+auto is_nondigit(char c)
+ -> bool
+{
+ return
+ isalpha(c)
+ || c == '_'
+ ;
+};
+
+//G identifier-start:
+//G nondigit
+//G
+auto is_identifier_start(char c)
+ -> bool
+{
+ return is_nondigit(c);
+}
+
+//G identifier-continue:
+//G digit
+//G nondigit
+//G
+auto is_identifier_continue(char c)
+ -> bool
+{
+ return
+ is_digit(c)
+ || is_nondigit(c)
+ ;
+}
+
+//G identifier:
+//G '__identifier__' keyword [Note: without whitespace before the keyword]
+//G identifier-start
+//G identifier identifier-continue
+//G 'operator' operator
+//G
+auto starts_with_identifier(std::string_view s)
+ -> int
+{
+ if (is_identifier_start(s[0])) {
+ auto j = 1;
+ while (
+ j < std::ssize(s)
+ && is_identifier_continue(s[j])
+ )
+ {
+ ++j;
+ }
+ return j;
+ }
+ return 0;
+};
+
+
+// Helper to allow one of the above or a digit separator
+// Example: is_separator_or( is_binary_digit (c) )
+//
+auto is_separator_or(auto pred, char c)
+ -> bool
+{
+ return
+ c == '\''
+ || pred(c)
+ ;
+}
+
+
+// Bool to string
+//
+template<typename T>
+ requires std::is_same_v<T, std::string>
+auto _as(bool b)
+ -> T
+{
+ return b ? "true" : "false";
+}
+
+
+// Explicit cast
+//
+template<typename T>
+auto _as(auto x)
+ -> T
+{
+ return T(x);
+}
+
+
+// String path prefix from filename
+//
+auto strip_path(std::string const& file)
+ -> std::string
+{
+ auto i = std::ssize(file)-1;
+ while (
+ i >= 0
+ && file[i] != '\\'
+ && file[i] != '/'
+ )
+ {
+ --i;
+ }
+ return {file, _as<size_t>(i+1)};
+}
+
+
+//-----------------------------------------------------------------------
+//
+// Misc helpers
+//
+//-----------------------------------------------------------------------
+//
+auto replace_all(std::string& s, std::string_view what, std::string_view with)
+{
+ for (
+ std::string::size_type pos{};
+ s.npos != (pos = s.find(what.data(), pos, what.length()));
+ pos += with.length()
+ )
+ {
+ s.replace(pos, what.length(), with.data(), with.length());
+ }
+ return s;
+}
+
+
+auto to_upper(char c)
+ -> char
+{
+ // C toupper is only not-UB in [0,127] and returns the wrong type,
+ // so wrap the range check and the type cast here in one place...
+ // note the 126 (not 127) is intentional to avoid a GCC warning
+ if (0 <= c && c <= 126) { return static_cast<char>(std::toupper(c)); }
+ // else
+ return c;
+}
+
+
+auto to_upper_and_underbar(std::string_view s)
+ -> std::string
+{
+ auto ret = std::string{s};
+ for (char& c : ret) {
+ if (std::isalnum(c)) { c = to_upper(c); }
+ else { c = '_'; }
+ }
+ return ret;
+}
+
+
+auto is_empty_or_a_decimal_number(std::string_view s)
+ -> bool
+{
+ auto size = std::ssize(s);
+ if (size == 0) { return true; }
+
+ auto i = 0;
+ while (i < size && isspace(s[i]) ) { ++i; }
+ while (i < size && isdigit(s[i]) ) { ++i; }
+ while (i < size && isspace(s[i]) ) { ++i; }
+ return i == size;
+}
+
+
+auto starts_with(
+ std::string const& s,
+ std::string_view sv
+)
+ -> bool
+{
+ return std::string_view(s).starts_with(sv);
+}
+
+
+auto contains(
+ auto const& range,
+ auto const& value
+)
+ -> bool
+{
+ return std::find(
+ range.begin(),
+ range.end(),
+ value
+ )
+ != range.end();
+}
+
+auto contains(
+ std::string const& s,
+ auto const& value
+)
+ -> bool
+{
+ return s.find(value) != s.npos;
+}
+
+
+// In keep trying to write string+string_view, and it ought to Just Work without
+// the current workarounds. Not having that is a minor impediment to using safe
+// and efficient string_views, which we should be encouraging. So for my own use
+// and to remove that minor impediment to writing safe and efficient code, I'm
+// just going to add this until we get P2591 in C++26(?) -- See: wg21.link/p2591
+//
+template<class charT, class traits, class Allocator>
+[[nodiscard]] constexpr auto operator+(
+ std::basic_string<charT, traits, Allocator> lhs,
+ std::type_identity_t<std::basic_string_view<charT, traits>> rhs
+ )
+ -> std::basic_string<charT, traits, Allocator>
+{
+ return lhs.append(rhs);
+}
+
+template<class charT, class traits, class Allocator>
+[[nodiscard]] constexpr auto operator+(
+ std::type_identity_t<std::basic_string_view<charT, traits>> lhs,
+ std::basic_string<charT, traits, Allocator> rhs
+ )
+ -> std::basic_string<charT, traits, Allocator>
+{
+ return rhs.insert(0, lhs);
+}
+
+
+//-----------------------------------------------------------------------
+//
+// Command line handling
+//
+//-----------------------------------------------------------------------
+//
+
+class cmdline_processor
+{
+ bool help_requested = false;
+
+ struct arg
+ {
+ int pos;
+ std::string text;
+
+ arg(int p, char* t) : pos{p}, text{t} { }
+ };
+ std::vector<arg> args;
+
+ using callback0 = void (*)();
+ using callback1 = void (*)(std::string const&);
+ struct flag
+ {
+ int group = 0;
+ std::string name;
+ int unique_prefix = 0;
+ std::string description;
+ callback0 handler0;
+ callback1 handler1;
+ std::string synonym;
+ bool opt_out;
+
+ flag(int g, std::string_view n, std::string_view d, callback0 h0, callback1 h1, std::string_view s, bool o)
+ : group{g}, name{n}, description{d}, handler0{h0}, handler1{h1}, synonym{s}, opt_out{o}
+ { }
+ };
+ std::vector<flag> flags;
+ int max_flag_length = 0;
+
+ std::unordered_map<int, std::string> labels = {
+ { 2, "Additional dynamic safety checks and contract information" },
+ { 4, "Support for constrained target environments" },
+ { 9, "Other options" }
+ };
+
+ // Define this in the main .cpp to avoid bringing <iostream> into the headers,
+ // so that we can't accidentally start depending on iostreams in the compiler body
+ static auto print(std::string_view, int width = 0)
+ -> void;
+
+public:
+ auto process_flags()
+ -> void
+ {
+ constexpr auto processed = -1;
+
+ // Calculate the unique prefixes
+ for (auto flag1 = flags.begin(); flag1 != flags.end(); ++flag1) {
+ for (auto flag2 = flag1+1; flag2 != flags.end(); ++flag2) {
+ int i = 0;
+ while (
+ i < std::ssize(flag1->name)
+ && i < std::ssize(flag2->name)
+ && flag1->name[i] != ' '
+ && flag2->name[i] != ' '
+ && flag1->name[i] == flag2->name[i]
+ )
+ {
+ ++i;
+ }
+ // Record that we found the unique prefix must be at least this long
+ flag1->unique_prefix = std::max( flag1->unique_prefix, i+1 );
+ flag2->unique_prefix = std::max( flag2->unique_prefix, i+1 );
+ }
+ }
+
+ // Look for matches
+ for (auto arg = args.begin(); arg != args.end(); ++arg)
+ {
+ // The arg should never be empty, but we're going to do a [0]
+ // subscript next so we should either check or assert
+ if (arg->text.empty()) {
+ continue;
+ }
+
+ // Provide a way to ignore the rest of the command line
+ // for the purpose of looking for switches
+ if (arg->text == "--") {
+ arg->pos = processed;
+ break;
+ }
+
+ for (auto& flag : flags) {
+ auto length_to_match = std::max(flag.unique_prefix, _as<int>(arg->text.length())-1);
+ if (
+ flag.opt_out
+ && arg->text.ends_with("-")
+ )
+ {
+ length_to_match = std::max(flag.unique_prefix, _as<int>(arg->text.length())-2);
+ }
+
+ // Allow a switch to start with either - or /
+ if (arg->text.starts_with("-" + flag.name.substr(0, length_to_match))
+ || arg->text.starts_with("/" + flag.name.substr(0, length_to_match))
+ || arg->text == "-" + flag.synonym
+ || arg->text == "/" + flag.synonym
+ )
+ {
+ assert(flag.handler0 || flag.handler1);
+
+ // If this is a standalone switch, just process it
+ if (flag.handler0) {
+ flag.handler0();
+ }
+
+ // Else
+ else {
+ // If this is a switch that could be suffixed with "-" to opt out
+ if (flag.opt_out) {
+ flag.handler1( arg->text.ends_with("-") ? "-" : "" );
+ }
+ // Else this is a switch that takes the next arg as its value, so pass that
+ else {
+ if (arg+1 == args.end()) {
+ print("Missing argument to option " + arg->text + " (try -help)\n");
+ help_requested = true;
+ }
+ arg->pos = processed;
+ ++arg; // move to next argument, which is the argument to this switch
+ flag.handler1(arg->text);
+ }
+ }
+
+ arg->pos = processed;
+ break;
+ }
+ }
+ }
+
+ std::erase_if( args, [=](auto& arg){ return arg.pos == processed; } );
+ }
+
+ auto print_help()
+ -> void
+ {
+ help_requested = true;
+
+ std::sort(
+ flags.begin(),
+ flags.end(),
+ [](auto& a, auto& b){ return a.group < b.group || (a.group == b.group && a.name < b.name); }
+ );
+
+ print("\nUsage: cppfront [options] file ...\n\nOptions:\n");
+ int last_group = -1;
+ for (auto& flag : flags) {
+ // Skip hidden flags
+ if (flag.name.front() == '_') {
+ continue;
+ }
+
+ if (last_group != flag.group) {
+ print("\n");
+ last_group = flag.group;
+ if (!labels[flag.group].empty()) {
+ print( labels[flag.group] + "\n");
+ }
+ }
+ print(" -");
+ auto n = flag.name.substr(0, flag.unique_prefix);
+ if (flag.unique_prefix < std::ssize(flag.name)) {
+ auto name_length = _as<int>(std::min(flag.name.find(' '), flag.name.size()));
+ n += "[";
+ n += flag.name.substr(flag.unique_prefix, name_length - flag.unique_prefix);
+ n += "]";
+ n += flag.name.substr(name_length);
+ }
+ if (flag.opt_out) {
+ n += "[-]";
+ }
+ if (!flag.synonym.empty()) {
+ n += ", -" + flag.synonym;
+ }
+ print(n, max_flag_length + 3);
+ print(flag.description);
+ print("\n");
+ }
+ }
+
+ auto add_flag(
+ int group,
+ std::string_view name,
+ std::string_view description,
+ callback0 handler0,
+ callback1 handler1,
+ std::string_view synonym,
+ bool opt_out
+ )
+ -> void
+ {
+ flags.emplace_back( group, name, description, handler0, handler1, synonym, opt_out );
+ auto length = std::ssize(name);
+ if (opt_out) { length += 3; } // space to print "[-]"
+ if (max_flag_length < length) {
+ max_flag_length = length;
+ }
+ }
+ struct register_flag {
+ register_flag(
+ int group,
+ std::string_view name,
+ std::string_view description,
+ callback0 handler0,
+ callback1 handler1 = {},
+ std::string_view synonym = {},
+ bool opt_out = false
+ );
+ };
+
+ auto set_args(
+ int argc,
+ char* argv[]
+ )
+ -> void
+ {
+ for (auto i = 1; i < argc; ++i) {
+ args.emplace_back( i, argv[i] );
+ }
+ }
+
+ auto help_was_requested()
+ -> bool
+ {
+ return help_requested;
+ }
+
+ auto arguments()
+ -> std::vector<arg>&
+ {
+ return args;
+ }
+
+ // This is used only by the owner of the 'main' branch
+ // to generate stable build version strings
+ auto gen_version()
+ -> void
+ {
+ help_requested = true;
+ std::string_view a = __DATE__;
+ std::string_view b = __TIME__;
+ std::unordered_map<std::string_view, char> m = { {"Jan",'1'}, {"Feb",'2'}, {"Mar",'3'}, {"Apr",'4'}, {"May",'5'}, {"Jun",'6'}, {"Jul",'7'}, {"Aug",'8'}, {"Sep",'9'}, {"Oct",'A'}, {"Nov",'B'}, {"Dec",'C'} };
+
+ auto stamp = std::to_string(atoi(&a[9])-15);
+ stamp += m[a.substr(0, 3)];
+ stamp += a.substr(4,2);
+ stamp += ":";
+ stamp += b.substr(0,2);
+ stamp += b.substr(3,2);
+ for (auto& c : stamp) { if (c == ' ') { c = '0'; } }
+ print( "\"" + stamp + "\"");
+ }
+
+ auto print_version()
+ -> void
+ {
+ help_requested = true;
+ print("\ncppfront compiler "
+ #include "version.info"
+ " Build "
+ #include "build.info"
+ );
+ print("\nCopyright(c) Herb Sutter All rights reserved\n");
+ print("\nSPDX-License-Identifier: CC-BY-NC-ND-4.0");
+ print("\n No commercial use");
+ print("\n No forks/derivatives");
+ print("\n Note: This license emphasizes that this is a personal");
+ print("\n experiment; it will be upgraded if that changes\n");
+ print("\nAbsolutely no warranty - try at your own risk\n");
+ }
+
+} cmdline;
+
+cmdline_processor::register_flag::register_flag(
+ int group,
+ std::string_view name,
+ std::string_view description,
+ callback0 handler0,
+ callback1 handler1,
+ std::string_view synonym,
+ bool opt_out
+)
+{
+ cmdline.add_flag( group, name, description, handler0, handler1, synonym, opt_out );
+}
+
+static cmdline_processor::register_flag cmd_help (
+ 0,
+ "help",
+ "Print help",
+ []{ cmdline.print_help(); },
+ nullptr,
+ "?"
+);
+
+static cmdline_processor::register_flag cmd_version(
+ 0,
+ "version",
+ "Print version information",
+ []{ cmdline.print_version(); }
+);
+
+static cmdline_processor::register_flag cmd_gen_version(
+ 0,
+ "_gen_version",
+ "Generate version information",
+ []{ cmdline.gen_version(); }
+);
+
+static auto flag_internal_debug = false;
+static cmdline_processor::register_flag cmd_internal_debug(
+ 0,
+ "_debug",
+ "Generate internal debug instrumentation",
+ []{ flag_internal_debug = true; }
+);
+
+
+//-----------------------------------------------------------------------
+//
+// Internal instrumentation
+//
+//-----------------------------------------------------------------------
+//
+
+class stackinstr
+{
+ struct entry
+ {
+ ptrdiff_t delta;
+ ptrdiff_t cumulative;
+ std::string_view func_name;
+ std::string_view file;
+ int line;
+ char* ptr;
+
+ entry(
+ std::string_view n,
+ std::string_view f,
+ int l,
+ char* p
+ )
+ : delta { entries.empty() ? 0 : std::abs(entries.back().ptr - p) }
+ , cumulative{ entries.empty() ? 0 : entries.back().cumulative + delta }
+ , func_name { n }
+ , file { f }
+ , line { l }
+ , ptr { p }
+ { }
+ };
+ static std::vector<entry> entries;
+ static std::vector<entry> deepest;
+ static std::vector<entry> largest;
+
+ static auto print(auto&& ee, std::string_view label) {
+ std::cout << "\n=== Stack debug information: " << label << " stack ===\n";
+ for (auto& e: ee)
+ if (e.ptr) {
+ std::cout
+ << " " << std::setw(6)
+ << ((std::abs(e.delta) < 1000000)? std::to_string(e.delta) : "-----") << " "
+ << std::setw(8)
+ << ((std::abs(e.delta) < 1000000)? std::to_string(e.cumulative) : "-------") << " "
+ << e.func_name << " (" << e.file << ":" << e.line << ")\n";
+ }
+ }
+
+public:
+ struct guard {
+ guard( std::string_view name, std::string_view file, int line, char* p ) {
+ if (flag_internal_debug) {
+ entries.emplace_back(name, file, line ,p);
+ if (ssize(deepest) < ssize(entries)) {
+ deepest = entries;
+ }
+ if (largest.empty() || largest.back().cumulative < entries.back().cumulative) {
+ largest = entries;
+ }
+ }
+ }
+ ~guard() {
+ if (flag_internal_debug) {
+ entries.pop_back();
+ }
+ }
+ };
+
+ static auto print_entries() { print( entries, "Current" ); }
+ static auto print_deepest() { print( deepest, "Deepest" ); }
+ static auto print_largest() { print( largest, "Largest" ); }
+};
+
+std::vector<stackinstr::entry> stackinstr::entries;
+std::vector<stackinstr::entry> stackinstr::deepest;
+std::vector<stackinstr::entry> stackinstr::largest;
+
+#define STACKINSTR stackinstr::guard _s_guard{ __func__, __FILE__, __LINE__, reinterpret_cast<char*>(&_s_guard) };
+
+
+}
+
+#endif