diff --git a/include/assert/assert.hpp b/include/assert/assert.hpp index 02270fbe..47584c44 100644 --- a/include/assert/assert.hpp +++ b/include/assert/assert.hpp @@ -103,6 +103,17 @@ #endif namespace libassert { + enum class literal_format { + integer_character = 1, + integer_decimal = 2, + integer_hex = 4, + integer_octal = 8, + integer_binary = 16, + float_decimal = 32, + float_hex = 64, + none = 0 + }; + enum class assert_type { debug_assertion, assertion, @@ -543,19 +554,8 @@ namespace libassert::detail { * C++ syntax analysis logic */ - enum class literal_format { - character, - dec, - hex, - octal, - binary, - none // needs to be at the end for sorting reasons - }; - [[nodiscard]] LIBASSERT_EXPORT std::string prettify_type(std::string type); - [[nodiscard]] LIBASSERT_EXPORT literal_format get_literal_format(const std::string& expression); - [[nodiscard]] LIBASSERT_EXPORT bool is_bitwise(std::string_view op); [[nodiscard]] LIBASSERT_EXPORT std::pair decompose_expression( @@ -596,52 +596,52 @@ namespace libassert::detail { } namespace stringification { - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(const std::string&, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(const std::string_view&, literal_format = literal_format::none); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(const std::string&); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(const std::string_view&); // without nullptr_t overload msvc (without /permissive-) will call stringify(bool) and mingw - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::nullptr_t, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(char, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(bool, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(short, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(int, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long long, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned short, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned int, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long long, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(float, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(double, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long double, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_code ec, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_condition ec, literal_format = literal_format::none); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::nullptr_t); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(char); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(bool); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(short); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(int); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long long); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned short); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned int); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(unsigned long long); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(float); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(double); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(long double); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_code); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::error_condition); #if __cplusplus >= 202002L - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::strong_ordering, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::weak_ordering, literal_format = literal_format::none); - [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::partial_ordering, literal_format = literal_format::none); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::strong_ordering); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::weak_ordering); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify(std::partial_ordering); #endif #ifdef __cpp_lib_expected template - [[nodiscard]] std::string stringify(const std::unexpected& x, literal_format fmt = literal_format::none) { - return "unexpected " + stringify(x.error(), fmt == literal_format::none ? literal_format::dec : fmt); + [[nodiscard]] std::string stringify(const std::unexpected& x) { + return "unexpected " + stringify(x.error()); } template - [[nodiscard]] std::string stringify(const std::expected& x, literal_format fmt = literal_format::none) { + [[nodiscard]] std::string stringify(const std::expected& x) { if(x.has_value()) { if constexpr(std::is_void_v) { return "expected void"; } else { - return "expected " + stringify(*x, fmt == literal_format::none ? literal_format::dec : fmt); + return "expected " + stringify(*x); } } else { - return "unexpected " + stringify(x.error(), fmt == literal_format::none ? literal_format::dec : fmt); + return "unexpected " + stringify(x.error()); } } #endif - [[nodiscard]] LIBASSERT_EXPORT std::string stringify_ptr(const void*, literal_format = literal_format::none); + [[nodiscard]] LIBASSERT_EXPORT std::string stringify_ptr(const void*); template class can_basic_stringify : public std::false_type {}; template class can_basic_stringify< @@ -693,7 +693,7 @@ namespace libassert::detail { || std::is_function>::value || !can_basic_stringify::value, int>::type = 0> LIBASSERT_ATTR_COLD [[nodiscard]] - std::string stringify(const T& t, [[maybe_unused]] literal_format fmt = literal_format::none) { + std::string stringify(const T& t) { if constexpr( has_stream_overload::value && !is_string_type && !std::is_pointer::type>>::value @@ -711,7 +711,7 @@ namespace libassert::detail { if(it != begin_it) { str += ", "; } - str += stringify(*it, literal_format::dec); + str += stringify(*it); } str += "]"; return str; @@ -726,7 +726,7 @@ namespace libassert::detail { } } return prettify_type(std::string(type_name())) + ": " - + stringify_ptr(reinterpret_cast(t), fmt); + + stringify_ptr(reinterpret_cast(t)); } else if constexpr(is_tuple_like::value) { return stringify_tuple_like(t); } @@ -747,10 +747,13 @@ namespace libassert::detail { // I'm going to assume at least one index because is_tuple_like requires index 0 to exist template std::string stringify_tuple_like(const T& t, std::index_sequence) { - using lf = literal_format; using stringification::stringify; // ADL return "[" - + (stringify(std::get<0>(t), lf::dec) + ... + (", " + stringify(std::get(t), lf::dec))) + + ( + stringify(std::get<0>(t)) + + ... + + (", " + stringify(std::get(t))) + ) + "]"; } } @@ -759,57 +762,53 @@ namespace libassert::detail { * assert diagnostics generation */ - constexpr size_t format_arr_length = 5; + enum class stringification_tag { + simple, + arithmetic + }; + + struct LIBASSERT_EXPORT stringification_wrapper { + stringification_tag tag; + std::string str; + }; // TODO: Not yet happy with the naming of this function / how it's used template LIBASSERT_ATTR_COLD [[nodiscard]] - std::string generate_stringification(const T& v, literal_format fmt = literal_format::none) { + stringification_wrapper generate_stringification(const T& v) { using stringification::stringify; // ADL - if constexpr((stringification::adl::is_printable_container::value && !is_string_type)) { - using std::size; // ADL - return prettify_type(std::string(type_name())) - + " [size: " + std::to_string(size(v)) + "]: " + stringify(v, fmt); - } else if constexpr(stringification::is_tuple_like::value) { - return prettify_type(std::string(type_name())) + ": " + stringify(v, fmt); - } else { - return stringify(v, fmt); - } - } - - template - LIBASSERT_ATTR_COLD [[nodiscard]] - std::vector generate_stringifications(const T& v, const literal_format (&formats)[format_arr_length]) { if constexpr((std::is_arithmetic>::value || std::is_enum>::value) && !isa) { - std::vector vec; - for(literal_format fmt : formats) { - if(fmt == literal_format::none) { break; } - using stringification::stringify; // ADL - // TODO: consider pushing empty fillers to keep columns aligned later on? Does not - // matter at the moment because floats only have decimal and hex literals but could - // if more formats are added. - vec.push_back(stringify(v, fmt)); - } - return vec; + return { stringification_tag::arithmetic, stringify(v) }; } else { - return { generate_stringification(v) }; + if constexpr((stringification::adl::is_printable_container::value && !is_string_type)) { + using std::size; // ADL + return { + stringification_tag::simple, + prettify_type(std::string(type_name())) + + " [size: " + std::to_string(size(v)) + "]: " + stringify(v) + }; + } else if constexpr(stringification::is_tuple_like::value) { + return { + stringification_tag::simple, prettify_type(std::string(type_name())) + ": " + stringify(v) + }; + } else { + return { stringification_tag::simple, stringify(v) }; + } } } struct LIBASSERT_EXPORT binary_diagnostics_descriptor { - std::vector lstrings; - std::vector rstrings; + stringification_wrapper lstring; + stringification_wrapper rstring; std::string a_str; std::string b_str; - bool multiple_formats; bool present = false; binary_diagnostics_descriptor(); // = default; in the .cpp binary_diagnostics_descriptor( - std::vector& lstrings, - std::vector& rstrings, - std::string a_str, - std::string b_str, - bool multiple_formats + stringification_wrapper&& lstring, + stringification_wrapper&& rstring, + const char* a_str, + const char* b_str ); compl binary_diagnostics_descriptor(); // = default; in the .cpp binary_diagnostics_descriptor(const binary_diagnostics_descriptor&) = delete; @@ -819,8 +818,6 @@ namespace libassert::detail { operator=(binary_diagnostics_descriptor&&) noexcept(LIBASSERT_GCC_ISNT_STUPID); // = default; in the .cpp }; - LIBASSERT_EXPORT void sort_and_dedup(literal_format(&)[format_arr_length]); - template LIBASSERT_ATTR_COLD [[nodiscard]] binary_diagnostics_descriptor generate_binary_diagnostic( @@ -830,29 +827,12 @@ namespace libassert::detail { const char* b_str, std::string_view op ) { - using lf = literal_format; - // Note: op - // figure out what information we need to print in the where clause - // find all literal formats involved (literal_format::dec included for everything) - auto lformat = get_literal_format(a_str); - auto rformat = get_literal_format(b_str); - // formerly used std::set here, now using array + sorting, `none` entries will be at the end and ignored - constexpr bool either_is_character = isa || isa; - constexpr bool either_is_arithmetic = is_arith_not_bool_char || is_arith_not_bool_char; - lf formats[format_arr_length] = { - either_is_arithmetic ? lf::dec : lf::none, - lformat, rformat, // ↓ always display binary for bitwise - is_bitwise(op) ? lf::binary : lf::none, - either_is_character ? lf::character : lf::none + return binary_diagnostics_descriptor { + generate_stringification(a), + generate_stringification(b), + a_str, + b_str }; - sort_and_dedup(formats); // print in specific order, avoid duplicates - if(formats[0] == lf::none) { - formats[0] = lf::dec; // if no formats apply just print everything default, TODO this is a bit of a hack - } - // generate raw strings for given formats, without highlighting - std::vector lstrings = generate_stringifications(a, formats); - std::vector rstrings = generate_stringifications(b, formats); - return binary_diagnostics_descriptor { lstrings, rstrings, a_str, b_str, formats[1] != lf::none }; } #define LIBASSERT_X(x) #x @@ -914,7 +894,7 @@ namespace libassert::detail { return; } } - entry.entries.push_back({ args_strings[i], generate_stringification(t, literal_format::dec) }); + entry.entries.push_back({ args_strings[i], generate_stringification(t).str }); } } } @@ -1013,8 +993,7 @@ namespace libassert::utility { template [[nodiscard]] std::string stringify(const T& t) { using detail::stringification::stringify; // ADL - using lf = detail::literal_format; - return stringify(t, detail::isa ? lf::character : lf::dec); + return stringify(t); } } diff --git a/src/analysis.cpp b/src/analysis.cpp index b7b954e4..54e3a08b 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -132,7 +132,6 @@ namespace libassert::detail { std::unordered_set bitwise_operators = { "^", "&", "|", "^=", "&=", "|=", "xor", "bitand", "bitor", "and_eq", "or_eq", "xor_eq" }; - std::vector> literal_formats; private: LIBASSERT_ATTR_COLD @@ -259,16 +258,6 @@ namespace libassert::detail { #endif rules[i] = { rules_raw[i].first, std::regex(str) }; } - // setup literal format rules - literal_formats = { - { std::regex(int_binary), literal_format::binary }, - { std::regex(int_octal), literal_format::octal }, - { std::regex(int_decimal), literal_format::dec }, - { std::regex(int_hex), literal_format::hex }, - { std::regex(float_decimal), literal_format::dec }, - { std::regex(float_hex), literal_format::hex }, - { std::regex(char_literal), literal_format::character } - }; // generate precedence table // bottom few rows of the precedence table: const std::unordered_map> precedences = { @@ -434,16 +423,6 @@ namespace libassert::detail { return {{"", expression}}; } - LIBASSERT_ATTR_COLD - literal_format get_literal_format(const std::string& expression) { - for(auto& [ re, type ] : literal_formats) { - if(std::regex_match(expression, re)) { - return type; - } - } - return literal_format::none; // not a literal - } - LIBASSERT_ATTR_COLD static token_t find_last_non_ws(const std::vector& tokens, size_t i) { // returns empty token_e::whitespace on failure @@ -746,10 +725,6 @@ namespace libassert::detail { #endif } - LIBASSERT_ATTR_COLD literal_format get_literal_format(const std::string& expression) { - return analysis::get().get_literal_format(expression); - } - LIBASSERT_ATTR_COLD std::string trim_suffix(const std::string& expression) { return expression.substr(0, expression.find_last_not_of("FfUuLlZz") + 1); } diff --git a/src/assert.cpp b/src/assert.cpp index 60553ee3..5675b33b 100644 --- a/src/assert.cpp +++ b/src/assert.cpp @@ -221,111 +221,70 @@ namespace libassert::detail { } namespace stringification { - LIBASSERT_ATTR_COLD std::string stringify(const std::string& value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(const std::string& value) { return escape_string(value, '"'); } - LIBASSERT_ATTR_COLD std::string stringify(const std::string_view& value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(const std::string_view& value) { return escape_string(value, '"'); } - LIBASSERT_ATTR_COLD std::string stringify(std::nullptr_t, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(std::nullptr_t) { return "nullptr"; } - LIBASSERT_ATTR_COLD std::string stringify(char value, literal_format fmt) { - if(fmt == literal_format::character) { - return escape_string({&value, 1}, '\''); - } else { - return stringify((int)value, fmt); - } + LIBASSERT_ATTR_COLD std::string stringify(char value) { + return escape_string({&value, 1}, '\''); } - LIBASSERT_ATTR_COLD std::string stringify(bool value, literal_format) { - return value ? "true" : "false"; + LIBASSERT_ATTR_COLD std::string stringify(bool value) { + return value ? "true" : "false"; } template, int>::type = 0> LIBASSERT_ATTR_COLD [[nodiscard]] - static std::string stringify_integral(T value, literal_format fmt) { + static std::string stringify_integral(T value) { std::ostringstream oss; - switch(fmt) { - case literal_format::character: - if(cmp_greater_equal(value, std::numeric_limits::min()) - && cmp_less_equal(value, std::numeric_limits::max())) { - return stringify(static_cast(value), literal_format::character); - } else { - return ""; - } - case literal_format::dec: - break; - case literal_format::hex: - oss<(value); - goto r; - default: - LIBASSERT_PRIMITIVE_ASSERT(false, "unexpected literal format requested for printing"); - } oss<::value, int>::type = 0> LIBASSERT_ATTR_COLD [[nodiscard]] - static std::string stringify_floating_point(T value, literal_format fmt) { + static std::string stringify_floating_point(T value) { std::ostringstream oss; - switch(fmt) { - case literal_format::character: - return ""; - case literal_format::dec: - break; - case literal_format::hex: - // apparently std::hexfloat automatically prepends "0x" while std::hex does not - oss<::max_digits10)<= 202002L - LIBASSERT_ATTR_COLD std::string stringify(std::strong_ordering value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(std::strong_ordering value) { if(value == std::strong_ordering::less) return "std::strong_ordering::less"; if(value == std::strong_ordering::equivalent) return "std::strong_ordering::equivalent"; if(value == std::strong_ordering::equal) return "std::strong_ordering::equal"; if(value == std::strong_ordering::greater) return "std::strong_ordering::greater"; return "Unknown std::strong_ordering value"; } - LIBASSERT_ATTR_COLD std::string stringify(std::weak_ordering value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(std::weak_ordering value) { if(value == std::weak_ordering::less) return "std::weak_ordering::less"; if(value == std::weak_ordering::equivalent) return "std::weak_ordering::equivalent"; if(value == std::weak_ordering::greater) return "std::weak_ordering::greater"; return "Unknown std::weak_ordering value"; } - LIBASSERT_ATTR_COLD std::string stringify(std::partial_ordering value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify(std::partial_ordering value) { if(value == std::partial_ordering::less) return "std::partial_ordering::less"; if(value == std::partial_ordering::equivalent) return "std::partial_ordering::equivalent"; if(value == std::partial_ordering::greater) return "std::partial_ordering::greater"; @@ -378,7 +337,7 @@ namespace libassert::detail { } #endif - LIBASSERT_ATTR_COLD std::string stringify_ptr(const void* value, literal_format) { + LIBASSERT_ATTR_COLD std::string stringify_ptr(const void* value) { if(value == nullptr) { return "nullptr"; } @@ -740,17 +699,15 @@ namespace libassert::detail { LIBASSERT_ATTR_COLD binary_diagnostics_descriptor::binary_diagnostics_descriptor() = default; LIBASSERT_ATTR_COLD binary_diagnostics_descriptor::binary_diagnostics_descriptor( - std::vector& _lstrings, - std::vector& _rstrings, - std::string _a_str, - std::string _b_str, - bool _multiple_formats + stringification_wrapper&& _lstring, + stringification_wrapper&& _rstring, + const char* _a_str, + const char* _b_str ): - lstrings(_lstrings), - rstrings(_rstrings), - a_str(std::move(_a_str)), - b_str(std::move(_b_str)), - multiple_formats(_multiple_formats), + lstring(std::move(_lstring)), + rstring(std::move(_rstring)), + a_str(_a_str), + b_str(_b_str), present(true) {} LIBASSERT_ATTR_COLD binary_diagnostics_descriptor::~binary_diagnostics_descriptor() = default; LIBASSERT_ATTR_COLD @@ -805,12 +762,12 @@ namespace libassert::detail { constexpr size_t where_indent = 8; LIBASSERT_ATTR_COLD [[nodiscard]] - std::string print_binary_diagnostics(size_t term_width, binary_diagnostics_descriptor& diagnostics) { - auto& [ lstrings, rstrings, a_sstr, b_sstr, multiple_formats, _ ] = diagnostics; + std::string print_binary_diagnostics(size_t term_width, const binary_diagnostics_descriptor& diagnostics) { + const auto& [ lstring, rstring, a_sstr, b_sstr, _ ] = diagnostics; + std::vector lstrings = { lstring.str }; + std::vector rstrings = { rstring.str }; const std::string& a_str = a_sstr; const std::string& b_str = b_sstr; - LIBASSERT_PRIMITIVE_ASSERT(!lstrings.empty()); - LIBASSERT_PRIMITIVE_ASSERT(!rstrings.empty()); // pad all columns where there is overlap // TODO: Use column printer instead of manual padding. for(size_t i = 0; i < std::min(lstrings.size(), rstrings.size()); i++) { @@ -823,8 +780,8 @@ namespace libassert::detail { } // determine whether we actually gain anything from printing a where clause (e.g. exclude "1 => 1") const struct { bool left, right; } has_useful_where_clause = { - multiple_formats || lstrings.size() > 1 || (a_str != lstrings[0] && trim_suffix(a_str) != lstrings[0]), - multiple_formats || rstrings.size() > 1 || (b_str != rstrings[0] && trim_suffix(b_str) != rstrings[0]) + /*multiple_formats ||*/ lstrings.size() > 1 || (a_str != lstrings[0] && trim_suffix(a_str) != lstrings[0]), + /*multiple_formats ||*/ rstrings.size() > 1 || (b_str != rstrings[0] && trim_suffix(b_str) != rstrings[0]) }; // print where clause std::string where; @@ -869,20 +826,6 @@ namespace libassert::detail { return where; } - LIBASSERT_ATTR_COLD - void sort_and_dedup(literal_format (&formats)[format_arr_length]) { - std::sort(std::begin(formats), std::end(formats)); - size_t write_index = 1, read_index = 1; - for(; read_index < std::size(formats); read_index++) { - if(formats[read_index] != formats[write_index - 1]) { - formats[write_index++] = formats[read_index]; - } - } - while(write_index < std::size(formats)) { - formats[write_index++] = literal_format::none; - } - } - LIBASSERT_ATTR_COLD [[nodiscard]] std::string print_extra_diagnostics( size_t term_width, diff --git a/tests/integration/integration.cpp b/tests/integration/integration.cpp index 828d13f4..71aa9d60 100644 --- a/tests/integration/integration.cpp +++ b/tests/integration/integration.cpp @@ -57,7 +57,7 @@ struct debug_print_customization { } }; -[[nodiscard]] std::string stringify(const debug_print_customization& p, libassert::detail::literal_format) { +[[nodiscard]] std::string stringify(const debug_print_customization& p) { return "(debug_print_customization = " + std::to_string(p.x) + ")"; }