From 81cd096fdf77822808d22102c478b50497c7193d Mon Sep 17 00:00:00 2001 From: Barry Revzin Date: Sun, 17 May 2020 10:30:57 -0500 Subject: [PATCH 1/2] Adding sentinel support to fmt::join(). --- include/fmt/format.h | 32 ++++++++++++++++++-------------- include/fmt/ranges.h | 4 ++-- test/ranges-test.cc | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 873e98bd4864..4fb44d481d27 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -291,6 +291,7 @@ template <> constexpr int num_bits() { // An approximation of iterator_t for pre-C++20 systems. template using iterator_t = decltype(std::begin(std::declval())); +template using sentinel_t = decltype(std::end(std::declval())); // Detect the iterator category of *any* given type in a SFINAE-friendly way. // Unfortunately, older implementations of std::iterator_traits are not safe @@ -3209,19 +3210,21 @@ template <> struct formatter { detail::dynamic_format_specs specs_; }; -template struct arg_join : detail::view { +template +struct arg_join : detail::view { It begin; - It end; + Sentinel end; basic_string_view sep; - arg_join(It b, It e, basic_string_view s) : begin(b), end(e), sep(s) {} + arg_join(It b, Sentinel e, basic_string_view s) + : begin(b), end(e), sep(s) {} }; -template -struct formatter, Char> +template +struct formatter, Char> : formatter::value_type, Char> { template - auto format(const arg_join& value, FormatContext& ctx) + auto format(const arg_join& value, FormatContext& ctx) -> decltype(ctx.out()) { using base = formatter::value_type, Char>; auto it = value.begin; @@ -3242,13 +3245,13 @@ struct formatter, Char> Returns an object that formats the iterator range `[begin, end)` with elements separated by `sep`. */ -template -arg_join join(It begin, It end, string_view sep) { +template +arg_join join(It begin, Sentinel end, string_view sep) { return {begin, end, sep}; } -template -arg_join join(It begin, It end, wstring_view sep) { +template +arg_join join(It begin, Sentinel end, wstring_view sep) { return {begin, end, sep}; } @@ -3269,14 +3272,15 @@ arg_join join(It begin, It end, wstring_view sep) { \endrst */ template -arg_join, char> join(const Range& range, - string_view sep) { +arg_join, detail::sentinel_t, char> +join(const Range& range, string_view sep) { return join(std::begin(range), std::end(range), sep); } template -arg_join, wchar_t> join(const Range& range, - wstring_view sep) { +arg_join, detail::sentinel_t, + wchar_t> +join(const Range& range, wstring_view sep) { return join(std::begin(range), std::end(range), sep); } diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index c58b97a1a50c..fd914c61ad93 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -368,13 +368,13 @@ FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, \endrst */ template -arg_join>, char> join( +arg_join join( std::initializer_list list, string_view sep) { return join(std::begin(list), std::end(list), sep); } template -arg_join>, wchar_t> join( +arg_join join( std::initializer_list list, wstring_view sep) { return join(std::begin(list), std::end(list), sep); } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 462ba7f604ae..d5dc2595cd2b 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -139,3 +139,18 @@ TEST(RangesTest, FormatStringLike) { EXPECT_EQ("foo", fmt::format("{}", string_like())); } #endif // FMT_USE_STRING_VIEW + +struct zstring_sentinel {}; +bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } +bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } +struct zstring { + const char* p; + const char* begin() const { return p; } + zstring_sentinel end() const { return {}; } +}; +TEST(RangesTest, JoinSentinel) { + zstring hello{"hello"}; + EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello)); + EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_"))); +} + From 53cb4fb8d906ee411a900a4ec847b23e575d3d2c Mon Sep 17 00:00:00 2001 From: Barry Revzin Date: Sun, 17 May 2020 12:32:47 -0500 Subject: [PATCH 2/2] Surprised gcc compiled this. --- include/fmt/ranges.h | 12 +++++++----- test/ranges-test.cc | 1 - 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index fd914c61ad93..c48f1727d5d6 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -261,7 +261,9 @@ struct formatter 0) { if (formatting.add_prepostfix_space) *out++ = ' '; out = detail::copy(formatting.delimiter, out); @@ -368,14 +370,14 @@ FMT_CONSTEXPR tuple_arg_join join(const std::tuple& tuple, \endrst */ template -arg_join join( - std::initializer_list list, string_view sep) { +arg_join join(std::initializer_list list, + string_view sep) { return join(std::begin(list), std::end(list), sep); } template -arg_join join( - std::initializer_list list, wstring_view sep) { +arg_join join(std::initializer_list list, + wstring_view sep) { return join(std::begin(list), std::end(list), sep); } diff --git a/test/ranges-test.cc b/test/ranges-test.cc index d5dc2595cd2b..46208e8df3b9 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -153,4 +153,3 @@ TEST(RangesTest, JoinSentinel) { EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello)); EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_"))); } -