From acc60e68b86b6aab49f39c32a6b9fd371311feb9 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 14 Jan 2024 11:45:04 -0500 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=89=20Array=20overload=20safety=20?= =?UTF-8?q?for=20base.h=20only?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/fmt/base.h | 59 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index fc0ea7b9505c..1b8f38f01fff 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -259,6 +259,8 @@ # define FMT_UNICODE !FMT_MSC_VERSION #endif +#define FMT_FWD(...) static_cast(__VA_ARGS__) + // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") #if !defined(__OPTIMIZE__) && !defined(__CUDACC__) @@ -2809,15 +2811,43 @@ using format_string = basic_format_string...>; inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } #endif +template +using format_string = basic_format_string...>; /** Formats a string and writes the output to ``out``. */ template ::value)> -auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { + FMT_ENABLE_IF(detail::is_output_iterator, + char>::value)> +auto vformat_to(OutputIt&& out, string_view fmt, format_args args) + -> remove_cvref_t { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, fmt, args, {}); return detail::get_iterator(buf, out); } +template +struct format_to_result { + /** Iterator past the end of the last write. */ + OutputIt out; + /** Iterator to the end of the output range. */ + OutputSen out_last; + + FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; } + FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; } + FMT_CONSTEXPR operator OutputIt&&() && noexcept { + return static_cast(out); + } +}; + +/** Formats a string and writes the output to ``out``. */ +template +auto vformat_to(char (&out)[Size], string_view fmt, format_args args) + -> format_to_result { + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, Size); + detail::vformat_to(buf, fmt, args, {}); + return {out + buf.count(), out + Size}; +} + /** \rst Formats ``args`` according to specifications in ``fmt``, writes the result to @@ -2831,9 +2861,28 @@ auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { \endrst */ template ::value)> -FMT_INLINE auto format_to(OutputIt out, format_string fmt, T&&... args) - -> OutputIt { + FMT_ENABLE_IF(detail::is_output_iterator, + char>::value)> +FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) + -> remove_cvref_t { + return vformat_to(FMT_FWD(out), fmt, fmt::make_format_args(args...)); +} + +/** + \rst + Formats ``args`` according to specifications in ``fmt``, writes the result to + the output iterator ``out`` and returns the iterator past the end of the output + range. `format_to` does not append a terminating null character. + + **Example**:: + + auto out = std::vector(); + fmt::format_to(std::back_inserter(out), "{}", 42); + \endrst + */ +template +FMT_INLINE auto format_to(char (&out)[Size], format_string fmt, + T&&... args) -> format_to_result { return vformat_to(out, fmt, fmt::make_format_args(args...)); } From c2488d130491f503d4509cbe177ea5dffeb078cf Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 14 Jan 2024 11:51:28 -0500 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=9B=A0=20Fix=20double-declaration=20o?= =?UTF-8?q?f=20format=5Fstring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit — 💬 "My bad; botched copy-paste job" --- include/fmt/base.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index 1b8f38f01fff..ab915c5aa20e 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2811,8 +2811,6 @@ using format_string = basic_format_string...>; inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } #endif -template -using format_string = basic_format_string...>; /** Formats a string and writes the output to ``out``. */ template , From 45595fd1486f0f5062da5d4552cf67ed8e2c961d Mon Sep 17 00:00:00 2001 From: ThePhD Date: Mon, 15 Jan 2024 22:05:59 -0500 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=8E=89=20format=5Fto=20array=20test?= =?UTF-8?q?=20suite=20in=20base-test!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/fmt/base.h | 69 ++++++++++++++++++---------------------------- test/base-test.cc | 43 ++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index ab915c5aa20e..d6ea51738930 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2822,30 +2822,6 @@ auto vformat_to(OutputIt&& out, string_view fmt, format_args args) return detail::get_iterator(buf, out); } -template -struct format_to_result { - /** Iterator past the end of the last write. */ - OutputIt out; - /** Iterator to the end of the output range. */ - OutputSen out_last; - - FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; } - FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; } - FMT_CONSTEXPR operator OutputIt&&() && noexcept { - return static_cast(out); - } -}; - -/** Formats a string and writes the output to ``out``. */ -template -auto vformat_to(char (&out)[Size], string_view fmt, format_args args) - -> format_to_result { - using traits = detail::fixed_buffer_traits; - auto buf = detail::iterator_buffer(out, Size); - detail::vformat_to(buf, fmt, args, {}); - return {out + buf.count(), out + Size}; -} - /** \rst Formats ``args`` according to specifications in ``fmt``, writes the result to @@ -2866,24 +2842,6 @@ FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) return vformat_to(FMT_FWD(out), fmt, fmt::make_format_args(args...)); } -/** - \rst - Formats ``args`` according to specifications in ``fmt``, writes the result to - the output iterator ``out`` and returns the iterator past the end of the output - range. `format_to` does not append a terminating null character. - - **Example**:: - - auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); - \endrst - */ -template -FMT_INLINE auto format_to(char (&out)[Size], format_string fmt, - T&&... args) -> format_to_result { - return vformat_to(out, fmt, fmt::make_format_args(args...)); -} - template struct format_to_n_result { /** Iterator past the end of the output range. */ OutputIt out; @@ -2916,6 +2874,33 @@ FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); } +template +struct format_to_result { + /** Iterator past the end of the last write. */ + OutputIt out; + /** Sentinel indicating the end of the output range. */ + OutputSen out_last; + + FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; } + FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; } + FMT_CONSTEXPR operator OutputIt&&() && noexcept { + return static_cast(out); + } +}; + +template +auto vformat_to(char (&out)[Size], string_view fmt, format_args args) + -> format_to_result { + format_to_n_result result = vformat_to_n(out, Size, fmt, args); + return {result.out, out + Size}; +} + +template +FMT_INLINE auto format_to(char (&out)[Size], format_string fmt, + T&&... args) -> format_to_result { + return vformat_to(out, fmt, fmt::make_format_args(args...)); +} + /** Returns the number of chars in the output of ``format(fmt, args...)``. */ template FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, diff --git a/test/base-test.cc b/test/base-test.cc index f01432ee092d..79022f1e8e3f 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -12,7 +12,7 @@ #include // INT_MAX #include // std::strlen #include // std::equal_to -#include // std::back_insert_iterator +#include // std::back_insert_iterator, std::distance #include // std::numeric_limits #include // std::string #include // std::is_same @@ -692,6 +692,47 @@ TEST(core_test, format_to) { EXPECT_EQ(s, "42"); } +TEST(core_test, format_to_c_array) { + char buffer[4]; + auto result = fmt::format_to(buffer, "{}", 12345); + EXPECT_EQ(4, std::distance(&buffer[0], result.out)); + EXPECT_EQ(0, std::distance(result.out, result.out_last)); + EXPECT_EQ(buffer + 4, result.out); + EXPECT_EQ("1234", fmt::string_view(buffer, 4)); + + result = fmt::format_to(buffer, "{:s}", "foobar"); + EXPECT_EQ(4, std::distance(&buffer[0], result.out)); + EXPECT_EQ(0, std::distance(result.out, result.out_last)); + EXPECT_EQ(buffer + 4, result.out); + EXPECT_EQ("foob", fmt::string_view(buffer, 4)); + + buffer[0] = 'x'; + buffer[1] = 'x'; + buffer[2] = 'x'; + buffer[3] = 'x'; + result = fmt::format_to(buffer, "{}", 'A'); + EXPECT_EQ(1, std::distance(&buffer[0], result.out)); + EXPECT_EQ(3, std::distance(result.out, result.out_last)); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ("Axxx", fmt::string_view(buffer, 4)); + + result = fmt::format_to(buffer, "{}{} ", 'B', 'C'); + EXPECT_EQ(3, std::distance(&buffer[0], result.out)); + EXPECT_EQ(1, std::distance(result.out, result.out_last)); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("BC x", fmt::string_view(buffer, 4)); + + result = fmt::format_to(buffer, "{}", "ABCDE"); + EXPECT_EQ(4, std::distance(&buffer[0], result.out)); + EXPECT_EQ(0, std::distance(result.out, result.out_last)); + EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); + + result = fmt::format_to(buffer, "{}", std::string(1000, '*')); + EXPECT_EQ(4, std::distance(&buffer[0], result.out)); + EXPECT_EQ(0, std::distance(result.out, result.out_last)); + EXPECT_EQ("****", fmt::string_view(buffer, 4)); +} + #ifdef __cpp_lib_byte TEST(core_test, format_byte) { auto s = std::string(); From 90b6cdfdc6a7c5754df3ab36408632f489a7f785 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Mon, 15 Jan 2024 22:13:21 -0500 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8E=A8=20Run=20clang-format,=20adjust?= =?UTF-8?q?=20format=5Fto=5Fresult=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/fmt/base.h | 2 +- test/base-test.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/fmt/base.h b/include/fmt/base.h index d6ea51738930..f5a7b37c7e23 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2876,7 +2876,7 @@ FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, template struct format_to_result { - /** Iterator past the end of the last write. */ + /** Iterator pointing to just after the last succesful write in the range. */ OutputIt out; /** Sentinel indicating the end of the output range. */ OutputSen out_last; diff --git a/test/base-test.cc b/test/base-test.cc index 79022f1e8e3f..2cd8ffedba70 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -9,6 +9,8 @@ #include "test-assert.h" // clang-format on +#include "fmt/base.h" + #include // INT_MAX #include // std::strlen #include // std::equal_to @@ -17,7 +19,6 @@ #include // std::string #include // std::is_same -#include "fmt/base.h" #include "gmock/gmock.h" using fmt::string_view;