From 50565f9853926501bd1c9bda07c6a98f4d940691 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 1 Jan 2024 16:21:21 -0800 Subject: [PATCH] Move misplaced join overloads to fmt/ranges.h --- include/fmt/core.h | 4 +-- include/fmt/format.h | 78 ---------------------------------------- include/fmt/ranges.h | 84 ++++++++++++++++++++++++++++++++++++++++---- include/fmt/xchar.h | 7 ++++ test/compile-test.cc | 1 + test/format-test.cc | 34 ------------------ test/os-test.cc | 3 +- test/ranges-test.cc | 36 ++++++++++++++++++- 8 files changed, 123 insertions(+), 124 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index acbf292e..35781df3 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -321,8 +321,8 @@ template FMT_CONSTEXPR void ignore_unused(const T&...) {} constexpr FMT_INLINE auto is_constant_evaluated( bool default_value = false) noexcept -> bool { // Workaround for incompatibility between libstdc++ consteval-based -// std::is_constant_evaluated() implementation and clang-14. -// https://github.com/fmtlib/fmt/issues/3247 +// std::is_constant_evaluated() implementation and clang-14: +// https://github.com/fmtlib/fmt/issues/3247. #if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ _GLIBCXX_RELEASE >= 12 && \ (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) diff --git a/include/fmt/format.h b/include/fmt/format.h index fbe344f2..0b7483af 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4224,84 +4224,6 @@ template struct nested_formatter { } }; -// DEPRECATED! join_view will be moved to ranges.h. -template -struct join_view : detail::view { - It begin; - Sentinel end; - basic_string_view sep; - - join_view(It b, Sentinel e, basic_string_view s) - : begin(b), end(e), sep(s) {} -}; - -template -struct formatter, Char> { - private: - using value_type = -#ifdef __cpp_lib_ranges - std::iter_value_t; -#else - typename std::iterator_traits::value_type; -#endif - formatter, Char> value_formatter_; - - public: - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { - return value_formatter_.parse(ctx); - } - - template - auto format(const join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { - auto it = value.begin; - auto out = ctx.out(); - if (it != value.end) { - out = value_formatter_.format(*it, ctx); - ++it; - while (it != value.end) { - out = detail::copy_str(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - out = value_formatter_.format(*it, ctx); - ++it; - } - } - return out; - } -}; - -/** - Returns a view that formats the iterator range `[begin, end)` with elements - separated by `sep`. - */ -template -auto join(It begin, Sentinel end, string_view sep) -> join_view { - return {begin, end, sep}; -} - -/** - \rst - Returns a view that formats `range` with elements separated by `sep`. - - **Example**:: - - std::vector v = {1, 2, 3}; - fmt::print("{}", fmt::join(v, ", ")); - // Output: "1, 2, 3" - - ``fmt::join`` applies passed format specifiers to the range elements:: - - fmt::print("{:02}", fmt::join(v, ", ")); - // Output: "01, 02, 03" - \endrst - */ -template -auto join(Range&& range, string_view sep) - -> join_view, detail::sentinel_t> { - return join(std::begin(range), std::end(range), sep); -} - /** \rst Converts *value* to ``std::string`` using the default format for type *T*. diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 3638fffb..a9cd60e5 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -574,6 +574,83 @@ struct formatter< Char> { }; +template +struct join_view : detail::view { + It begin; + Sentinel end; + basic_string_view sep; + + join_view(It b, Sentinel e, basic_string_view s) + : begin(b), end(e), sep(s) {} +}; + +template +struct formatter, Char> { + private: + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t; +#else + typename std::iterator_traits::value_type; +#endif + formatter, Char> value_formatter_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { + return value_formatter_.parse(ctx); + } + + template + auto format(const join_view& value, + FormatContext& ctx) const -> decltype(ctx.out()) { + auto it = value.begin; + auto out = ctx.out(); + if (it != value.end) { + out = value_formatter_.format(*it, ctx); + ++it; + while (it != value.end) { + out = detail::copy_str(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); + out = value_formatter_.format(*it, ctx); + ++it; + } + } + return out; + } +}; + +/** + Returns a view that formats the iterator range `[begin, end)` with elements + separated by `sep`. + */ +template +auto join(It begin, Sentinel end, string_view sep) -> join_view { + return {begin, end, sep}; +} + +/** + \rst + Returns a view that formats `range` with elements separated by `sep`. + + **Example**:: + + std::vector v = {1, 2, 3}; + fmt::print("{}", fmt::join(v, ", ")); + // Output: "1, 2, 3" + + ``fmt::join`` applies passed format specifiers to the range elements:: + + fmt::print("{:02}", fmt::join(v, ", ")); + // Output: "01, 02, 03" + \endrst + */ +template +auto join(Range&& range, string_view sep) + -> join_view, detail::sentinel_t> { + return join(std::begin(range), std::end(range), sep); +} + template struct tuple_join_view : detail::view { const std::tuple& tuple; basic_string_view sep; @@ -708,13 +785,6 @@ FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) return {tuple, sep}; } -template -FMT_CONSTEXPR auto join(const std::tuple& tuple, - basic_string_view sep) - -> tuple_join_view { - return {tuple, sep}; -} - /** \rst Returns an object that formats `initializer_list` with elements separated by diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index f609c5c4..de41aeea 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -11,6 +11,7 @@ #include #include "format.h" +#include "ranges.h" #ifndef FMT_STATIC_THOUSANDS_SEPARATOR # include @@ -96,6 +97,12 @@ auto join(std::initializer_list list, wstring_view sep) return join(std::begin(list), std::end(list), sep); } +template +auto join(const std::tuple& tuple, basic_string_view sep) + -> tuple_join_view { + return {tuple, sep}; +} + template ::value)> auto vformat(basic_string_view format_str, basic_format_args>> args) diff --git a/test/compile-test.cc b/test/compile-test.cc index 8551303e..1b6d2157 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -10,6 +10,7 @@ #include #include "fmt/chrono.h" +#include "fmt/ranges.h" #include "gmock/gmock.h" #include "gtest-extra.h" diff --git a/test/format-test.cc b/test/format-test.cc index 325bef99..76669e97 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1791,40 +1791,6 @@ TEST(format_test, nested_formatter) { enum test_enum { foo, bar }; auto format_as(test_enum e) -> int { return e; } -TEST(format_test, join) { - using fmt::join; - int v1[3] = {1, 2, 3}; - auto v2 = std::vector(); - v2.push_back(1.2f); - v2.push_back(3.4f); - void* v3[2] = {&v1[0], &v1[1]}; - - EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)"); - EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)"); - EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()"); - EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)"); - EXPECT_EQ("(+01.20, +03.40)", - fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", "))); - - EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3"); - - EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]), - fmt::format("{}", join(v3, v3 + 2, ", "))); - - EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)"); - EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)"); - - auto v4 = std::vector{foo, bar, foo}; - EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0"); -} - -#ifdef __cpp_lib_byte -TEST(format_test, join_bytes) { - auto v = std::vector{std::byte(1), std::byte(2), std::byte(3)}; - EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3"); -} -#endif - std::string vformat_message(int id, const char* format, fmt::format_args args) { auto buffer = fmt::memory_buffer(); fmt::format_to(fmt::appender(buffer), "[{}] ", id); diff --git a/test/os-test.cc b/test/os-test.cc index abc5d3ad..079ec920 100644 --- a/test/os-test.cc +++ b/test/os-test.cc @@ -252,8 +252,7 @@ TEST(ostream_test, move_while_holding_data) { TEST(ostream_test, print) { fmt::ostream out = fmt::output_file("test-file"); - out.print("The answer is {}.\n", - fmt::join(std::initializer_list{42}, ", ")); + out.print("The answer is {}.\n", 42); out.close(); file in("test-file", file::RDONLY); EXPECT_READ(in, "The answer is 42.\n"); diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 8ab66b33..2d755129 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -287,7 +287,7 @@ TEST(ranges_test, range) { EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]"); } -enum test_enum { foo }; +enum test_enum { foo, bar }; auto format_as(test_enum e) -> int { return e; } TEST(ranges_test, enum_range) { @@ -302,6 +302,40 @@ TEST(ranges_test, unformattable_range) { } #endif +TEST(ranges_test, join) { + using fmt::join; + int v1[3] = {1, 2, 3}; + auto v2 = std::vector(); + v2.push_back(1.2f); + v2.push_back(3.4f); + void* v3[2] = {&v1[0], &v1[1]}; + + EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)"); + EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)"); + EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()"); + EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)"); + EXPECT_EQ("(+01.20, +03.40)", + fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", "))); + + EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3"); + + EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]), + fmt::format("{}", join(v3, v3 + 2, ", "))); + + EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)"); + EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)"); + + auto v4 = std::vector{foo, bar, foo}; + EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0"); +} + +#ifdef __cpp_lib_byte +TEST(ranges_test, join_bytes) { + auto v = std::vector{std::byte(1), std::byte(2), std::byte(3)}; + EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3"); +} +#endif + #ifdef FMT_RANGES_TEST_ENABLE_JOIN TEST(ranges_test, join_tuple) { // Value tuple args.