From 81cd096fdf77822808d22102c478b50497c7193d Mon Sep 17 00:00:00 2001 From: Barry Revzin Date: Sun, 17 May 2020 10:30:57 -0500 Subject: [PATCH] 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 873e98bd..4fb44d48 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 c58b97a1..fd914c61 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 462ba7f6..d5dc2595 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, "_"))); +} +