From 7a8288d3a17b316280eba7f51978fa9f1aa88ca3 Mon Sep 17 00:00:00 2001 From: Jin S Date: Fri, 23 Feb 2024 14:24:07 -0500 Subject: [PATCH 1/9] initial commit --- include/fmt/ranges.h | 80 ++++++++++++++++++++++++++++++++++++++++---- test/ranges-test.cc | 5 +++ 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 03eb5184882d..128a7aa743c9 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -14,6 +14,7 @@ #include #include "base.h" +#include "format.h" FMT_BEGIN_NAMESPACE @@ -388,7 +389,8 @@ struct range_formatter< detail::string_literal{}; basic_string_view closing_bracket_ = detail::string_literal{}; - + bool is_string_format = false; + bool is_debug = false; public: FMT_CONSTEXPR range_formatter() {} @@ -415,19 +417,85 @@ struct range_formatter< set_brackets({}, {}); ++it; } + else { + bool check_for_s = false; + if (it != end && *it == '?') { + ++it; + detail::maybe_set_debug_format(underlying_, true); + set_brackets({}, {}); + check_for_s = true; + is_debug = true; + } + if (it != end && *it == 's') { + if (!std::is_same::value) { + report_error("invalid format specifier"); + } + if (!is_debug) { + set_brackets(detail::string_literal{}, detail::string_literal{}); + } + check_for_s = false; + is_string_format = true; + ++it; + + } + if (check_for_s) { + report_error("invalid format specifier"); + } + } - if (it != end && *it != '}') { - if (*it != ':') report_error("invalid format specifier"); + if (it != end && *it != '}') { + if (is_string_format || *it != ':') report_error("invalid format specifier"); ++it; } else { - detail::maybe_set_debug_format(underlying_, true); + if (!is_string_format) + detail::maybe_set_debug_format(underlying_, true); } ctx.advance_to(it); return underlying_.parse(ctx); } - template + template ::value, bool> = true> + auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { + detail::range_mapper> mapper; + auto out = ctx.out(); + out = detail::copy(opening_bracket_, out); + int i = 0; + auto it = detail::range_begin(range); + auto end = detail::range_end(range); + if (is_string_format) { + if (is_debug) { + auto buf = basic_memory_buffer(); + for (; it != end; ++it) { + auto&& item = *it; + buf.push_back(item); + } + format_specs spec_str{}; + spec_str.type = presentation_type::debug; + detail::write(out, basic_string_view(buf.data(),buf.size()), spec_str); + } + else { + for (; it != end; ++it) { + ctx.advance_to(out); + auto&& item = *it; + out = underlying_.format(mapper.map(item), ctx); + } + } + } + else { + for (; it != end; ++it) { + if (i > 0) out = detail::copy(separator_, out); + ctx.advance_to(out); + auto&& item = *it; + out = underlying_.format(mapper.map(item), ctx); + ++i; + } + } + out = detail::copy(closing_bracket_, out); + return out; + } + + template ::value), bool> = true> auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); @@ -439,7 +507,7 @@ struct range_formatter< if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + out = underlying_.format(mapper.map(item), ctx); ++i; } out = detail::copy(closing_bracket_, out); diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 74cbc6194c0f..db86e4161d90 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -58,8 +58,13 @@ TEST(ranges_test, format_vector) { EXPECT_EQ(fmt::format("{:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb"); auto vc = std::vector{'a', 'b', 'c'}; + auto vec = std::vector{'a', '\n', '\t'}; auto vvc = std::vector>{vc, vc}; EXPECT_EQ(fmt::format("{}", vc), "['a', 'b', 'c']"); + EXPECT_EQ(fmt::format("{:s}", vc), "\"abc\""); + EXPECT_EQ(fmt::format("{:?s}", vec), "\"a\\n\\t\""); + EXPECT_EQ(fmt::format("{:s}", vec), "\"a\n\t\""); + EXPECT_EQ(fmt::format("{::s}", vvc), "[\"abc\", \"abc\"]"); EXPECT_EQ(fmt::format("{}", vvc), "[['a', 'b', 'c'], ['a', 'b', 'c']]"); EXPECT_EQ(fmt::format("{:n}", vvc), "['a', 'b', 'c'], ['a', 'b', 'c']"); EXPECT_EQ(fmt::format("{:n:n}", vvc), "'a', 'b', 'c', 'a', 'b', 'c'"); From 181dbcf0b36a828919f6f03533232ab9dbe450bc Mon Sep 17 00:00:00 2001 From: Jin S Date: Fri, 23 Feb 2024 14:28:25 -0500 Subject: [PATCH 2/9] fix build --- include/fmt/ranges.h | 45 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 128a7aa743c9..ff3d3a47ce6e 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -391,6 +391,7 @@ struct range_formatter< detail::string_literal{}; bool is_string_format = false; bool is_debug = false; + public: FMT_CONSTEXPR range_formatter() {} @@ -416,8 +417,7 @@ struct range_formatter< if (it != end && *it == 'n') { set_brackets({}, {}); ++it; - } - else { + } else { bool check_for_s = false; if (it != end && *it == '?') { ++it; @@ -427,35 +427,36 @@ struct range_formatter< is_debug = true; } if (it != end && *it == 's') { - if (!std::is_same::value) { + if (!std::is_same::value) { report_error("invalid format specifier"); } if (!is_debug) { - set_brackets(detail::string_literal{}, detail::string_literal{}); + set_brackets(detail::string_literal{}, + detail::string_literal{}); } check_for_s = false; is_string_format = true; - ++it; - + ++it; } - if (check_for_s) { + if (check_for_s) { report_error("invalid format specifier"); - } + } } - if (it != end && *it != '}') { - if (is_string_format || *it != ':') report_error("invalid format specifier"); + if (it != end && *it != '}') { + if (is_string_format || *it != ':') + report_error("invalid format specifier"); ++it; } else { - if (!is_string_format) - detail::maybe_set_debug_format(underlying_, true); + if (!is_string_format) detail::maybe_set_debug_format(underlying_, true); } ctx.advance_to(it); return underlying_.parse(ctx); } - template ::value, bool> = true> + template ::value, bool> = true> auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); @@ -472,22 +473,21 @@ struct range_formatter< } format_specs spec_str{}; spec_str.type = presentation_type::debug; - detail::write(out, basic_string_view(buf.data(),buf.size()), spec_str); - } - else { + detail::write(out, basic_string_view(buf.data(), buf.size()), + spec_str); + } else { for (; it != end; ++it) { ctx.advance_to(out); auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + out = underlying_.format(mapper.map(item), ctx); } } - } - else { + } else { for (; it != end; ++it) { if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + out = underlying_.format(mapper.map(item), ctx); ++i; } } @@ -495,7 +495,8 @@ struct range_formatter< return out; } - template ::value), bool> = true> + template ::value), bool> = true> auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); @@ -507,7 +508,7 @@ struct range_formatter< if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); + out = underlying_.format(mapper.map(item), ctx); ++i; } out = detail::copy(closing_bracket_, out); From d17e9c96ed2fb4b843bd4adedf7b9c2f5ff6de04 Mon Sep 17 00:00:00 2001 From: Jin S Date: Fri, 23 Feb 2024 14:54:44 -0500 Subject: [PATCH 3/9] format --- include/fmt/ranges.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index ff3d3a47ce6e..fc1d58f5bd38 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -473,8 +473,8 @@ struct range_formatter< } format_specs spec_str{}; spec_str.type = presentation_type::debug; - detail::write(out, basic_string_view(buf.data(), buf.size()), - spec_str); + detail::write( + out, basic_string_view(buf.data(), buf.size()), spec_str); } else { for (; it != end; ++it) { ctx.advance_to(out); From 556e412d3ce8ac9d309062f381787f6251819d4a Mon Sep 17 00:00:00 2001 From: Jin S Date: Mon, 26 Feb 2024 21:00:34 -0500 Subject: [PATCH 4/9] clean up --- include/fmt/ranges.h | 38 +++++--------------------------------- 1 file changed, 5 insertions(+), 33 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index fc1d58f5bd38..38a78be603a3 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -13,7 +13,6 @@ #include #include -#include "base.h" #include "format.h" FMT_BEGIN_NAMESPACE @@ -455,17 +454,14 @@ struct range_formatter< return underlying_.parse(ctx); } - template ::value, bool> = true> + template auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); - out = detail::copy(opening_bracket_, out); - int i = 0; auto it = detail::range_begin(range); auto end = detail::range_end(range); - if (is_string_format) { - if (is_debug) { + if constexpr (std::is_same::value) { + if (is_string_format && is_debug) { auto buf = basic_memory_buffer(); for (; it != end; ++it) { auto&& item = *it; @@ -473,39 +469,15 @@ struct range_formatter< } format_specs spec_str{}; spec_str.type = presentation_type::debug; - detail::write( + return detail::write( out, basic_string_view(buf.data(), buf.size()), spec_str); - } else { - for (; it != end; ++it) { - ctx.advance_to(out); - auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); - } - } - } else { - for (; it != end; ++it) { - if (i > 0) out = detail::copy(separator_, out); - ctx.advance_to(out); - auto&& item = *it; - out = underlying_.format(mapper.map(item), ctx); - ++i; } } - out = detail::copy(closing_bracket_, out); - return out; - } - template ::value), bool> = true> - auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { - detail::range_mapper> mapper; - auto out = ctx.out(); out = detail::copy(opening_bracket_, out); int i = 0; - auto it = detail::range_begin(range); - auto end = detail::range_end(range); for (; it != end; ++it) { - if (i > 0) out = detail::copy(separator_, out); + if (i > 0 && !is_string_format) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; out = underlying_.format(mapper.map(item), ctx); From 5ebe8c8a693c9e58339126abd922923908149371 Mon Sep 17 00:00:00 2001 From: Jin S Date: Tue, 27 Feb 2024 10:56:51 -0500 Subject: [PATCH 5/9] separate func for writing debug strings --- include/fmt/ranges.h | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 38a78be603a3..7768c23454b8 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -454,24 +454,36 @@ struct range_formatter< return underlying_.parse(ctx); } + template ::value, bool> = true> + auto write_debug_string(Output& out, Iter& it, IterEnd& end) const { + auto buf = basic_memory_buffer(); + for (; it != end; ++it) { + auto&& item = *it; + buf.push_back(item); + } + format_specs spec_str{}; + spec_str.type = presentation_type::debug; + detail::write(out, basic_string_view(buf.data(), buf.size()), + spec_str); + } + template ::value), bool> = true> + auto write_debug_string(Output& out, Iter& it, IterEnd& end) const { + detail::ignore_unused(out); + detail::ignore_unused(it); + detail::ignore_unused(end); + } + template auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { detail::range_mapper> mapper; auto out = ctx.out(); auto it = detail::range_begin(range); auto end = detail::range_end(range); - if constexpr (std::is_same::value) { - if (is_string_format && is_debug) { - auto buf = basic_memory_buffer(); - for (; it != end; ++it) { - auto&& item = *it; - buf.push_back(item); - } - format_specs spec_str{}; - spec_str.type = presentation_type::debug; - return detail::write( - out, basic_string_view(buf.data(), buf.size()), spec_str); - } + if (is_string_format && is_debug) { + write_debug_string(out, it, end); + return out; } out = detail::copy(opening_bracket_, out); From 6f6f222f4963c64e7bc99b475379b711f22f3ffb Mon Sep 17 00:00:00 2001 From: Jin S Date: Tue, 27 Feb 2024 11:05:08 -0500 Subject: [PATCH 6/9] fix return type --- include/fmt/ranges.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 7768c23454b8..04a0c927a595 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -456,7 +456,7 @@ struct range_formatter< template ::value, bool> = true> - auto write_debug_string(Output& out, Iter& it, IterEnd& end) const { + auto write_debug_string(Output& out, Iter& it, IterEnd& end) const -> Output { auto buf = basic_memory_buffer(); for (; it != end; ++it) { auto&& item = *it; @@ -464,15 +464,15 @@ struct range_formatter< } format_specs spec_str{}; spec_str.type = presentation_type::debug; - detail::write(out, basic_string_view(buf.data(), buf.size()), + return detail::write(out, basic_string_view(buf.data(), buf.size()), spec_str); } template ::value), bool> = true> - auto write_debug_string(Output& out, Iter& it, IterEnd& end) const { - detail::ignore_unused(out); + auto write_debug_string(Output& out, Iter& it, IterEnd& end) const -> Output { detail::ignore_unused(it); detail::ignore_unused(end); + return out; } template @@ -482,8 +482,7 @@ struct range_formatter< auto it = detail::range_begin(range); auto end = detail::range_end(range); if (is_string_format && is_debug) { - write_debug_string(out, it, end); - return out; + return write_debug_string(out, it, end); } out = detail::copy(opening_bracket_, out); From e36ec0410ae340d346b760920ea663bcb5b4bbc7 Mon Sep 17 00:00:00 2001 From: Jin S Date: Tue, 27 Feb 2024 11:06:21 -0500 Subject: [PATCH 7/9] format --- include/fmt/ranges.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 04a0c927a595..6e22d115eecb 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -464,8 +464,8 @@ struct range_formatter< } format_specs spec_str{}; spec_str.type = presentation_type::debug; - return detail::write(out, basic_string_view(buf.data(), buf.size()), - spec_str); + return detail::write( + out, basic_string_view(buf.data(), buf.size()), spec_str); } template ::value), bool> = true> From 645fde421dc48a02908f40de8ccbac58bdb2a103 Mon Sep 17 00:00:00 2001 From: Jin S Date: Mon, 4 Mar 2024 15:56:26 -0500 Subject: [PATCH 8/9] implement changes --- include/fmt/ranges.h | 69 ++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 6e22d115eecb..1a9cd72aa54a 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -412,42 +412,40 @@ struct range_formatter< FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); auto end = ctx.end(); + detail::maybe_set_debug_format(underlying_, true); + if (it == end) { + return underlying_.parse(ctx); + } - if (it != end && *it == 'n') { + switch (detail::to_ascii(*it)) { + case 'n': set_brackets({}, {}); ++it; - } else { - bool check_for_s = false; - if (it != end && *it == '?') { - ++it; - detail::maybe_set_debug_format(underlying_, true); - set_brackets({}, {}); - check_for_s = true; - is_debug = true; - } - if (it != end && *it == 's') { - if (!std::is_same::value) { - report_error("invalid format specifier"); - } - if (!is_debug) { - set_brackets(detail::string_literal{}, - detail::string_literal{}); - } - check_for_s = false; - is_string_format = true; - ++it; - } - if (check_for_s) { + break; + case '?': + is_debug = true; + set_brackets({}, {}); + ++it; + FMT_FALLTHROUGH; + case 's': + if (it == end || *it != 's' || !std::is_same::value) { report_error("invalid format specifier"); } + if (!is_debug) { + set_brackets(detail::string_literal{}, + detail::string_literal{}); + set_separator({}); + detail::maybe_set_debug_format(underlying_, false); + } + is_string_format = true; + ++it; + return it; } if (it != end && *it != '}') { - if (is_string_format || *it != ':') - report_error("invalid format specifier"); + if (*it != ':') report_error("invalid format specifier"); + detail::maybe_set_debug_format(underlying_, false); ++it; - } else { - if (!is_string_format) detail::maybe_set_debug_format(underlying_, true); } ctx.advance_to(it); @@ -455,23 +453,20 @@ struct range_formatter< } template ::value, bool> = true> + FMT_ENABLE_IF(std::is_same::value)> auto write_debug_string(Output& out, Iter& it, IterEnd& end) const -> Output { auto buf = basic_memory_buffer(); for (; it != end; ++it) { - auto&& item = *it; - buf.push_back(item); + buf.push_back(*it); } - format_specs spec_str{}; + format_specs spec_str; spec_str.type = presentation_type::debug; return detail::write( out, basic_string_view(buf.data(), buf.size()), spec_str); } template ::value), bool> = true> - auto write_debug_string(Output& out, Iter& it, IterEnd& end) const -> Output { - detail::ignore_unused(it); - detail::ignore_unused(end); + FMT_ENABLE_IF(!std::is_same::value)> + auto write_debug_string(Output& out, Iter&, IterEnd&) const -> Output { return out; } @@ -481,14 +476,14 @@ struct range_formatter< auto out = ctx.out(); auto it = detail::range_begin(range); auto end = detail::range_end(range); - if (is_string_format && is_debug) { + if (is_debug) { return write_debug_string(out, it, end); } out = detail::copy(opening_bracket_, out); int i = 0; for (; it != end; ++it) { - if (i > 0 && !is_string_format) out = detail::copy(separator_, out); + if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); auto&& item = *it; out = underlying_.format(mapper.map(item), ctx); From 5151ee499327ed7ac17d69df363db97e68ddaa63 Mon Sep 17 00:00:00 2001 From: Jin S Date: Wed, 6 Mar 2024 14:53:13 -0500 Subject: [PATCH 9/9] move error checking --- include/fmt/ranges.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 1a9cd72aa54a..af3609c0c6d1 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -426,9 +426,12 @@ struct range_formatter< is_debug = true; set_brackets({}, {}); ++it; + if (it == end || *it != 's') { + report_error("invalid format specifier"); + } FMT_FALLTHROUGH; case 's': - if (it == end || *it != 's' || !std::is_same::value) { + if (!std::is_same::value) { report_error("invalid format specifier"); } if (!is_debug) {