From 9427f15bef6e5d937d5a4919e5ed5281fa703005 Mon Sep 17 00:00:00 2001 From: stryku Date: Tue, 21 May 2019 21:03:57 +0200 Subject: [PATCH 1/9] Fixed issue with formatting to an array of chars --- include/fmt/prepare.h | 18 ++++++++------ test/prepare-test.cc | 57 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/include/fmt/prepare.h b/include/fmt/prepare.h index 0ac9218875a2..791858afdc92 100644 --- a/include/fmt/prepare.h +++ b/include/fmt/prepare.h @@ -217,7 +217,8 @@ class prepared_format { std::basic_string format(const Args&... args) const { basic_memory_buffer buffer; typedef back_insert_range> range; - this->vformat_to(range(buffer), make_args_checked(format_, args...)); + this->vformat_to(range(buffer), basic_format_args{ + make_args_checked(format_, args...)}); return to_string(buffer); } @@ -226,7 +227,8 @@ class prepared_format { std::back_insert_iterator out, const Args&... args) const { internal::container_buffer buffer(internal::get_container(out)); typedef back_insert_range> range; - this->vformat_to(range(buffer), make_args_checked(format_, args...)); + this->vformat_to(range(buffer), basic_format_args{ + make_args_checked(format_, args...)}); return out; } @@ -242,18 +244,20 @@ class prepared_format { inline typename buffer_context::iterator format_to( basic_memory_buffer& buf, const Args&... args) const { typedef back_insert_range> range; - return this->vformat_to(range(buf), make_args_checked(format_, args...)); + return this->vformat_to( + range(buf), + basic_format_args{make_args_checked(format_, args...)}); } private: typedef buffer_context context; - template - typename context::iterator vformat_to(Range out, - basic_format_args args) const { + template + auto vformat_to(Range out, basic_format_args args) const -> + typename Context::iterator { const auto format_view = internal::to_string_view(format_); basic_parse_context parse_ctx(format_view); - context ctx(out.begin(), args); + Context ctx(out.begin(), args); const auto& parts = parts_provider_.parts(); for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) { diff --git a/test/prepare-test.cc b/test/prepare-test.cc index 64fe98c11091..a210178b50b4 100644 --- a/test/prepare-test.cc +++ b/test/prepare-test.cc @@ -475,7 +475,7 @@ TEST(PrepareTest, CopyPreparedFormat_InternalStringViewsAreNotInvalidated) { } } -TEST(PepareTest, ReusedPreparedFormatType) { +TEST(PrepareTest, ReusedPreparedFormatType) { typedef fmt::prepared_format::type prepared_format; prepared_format prepared = fmt::prepare("The {} is {}."); @@ -637,3 +637,58 @@ TEST(PrepareTest, PassUserTypeFormat) { const auto prepared = fmt::prepare(user_format("test {}")); EXPECT_EQ("test 42", prepared.format(42)); } + +TEST(PrepareTest, FormatToArrayOfChars) { + char buffer[32] = {0}; + const auto prepared = fmt::prepare("4{}"); + prepared.format_to(buffer, 2); + EXPECT_EQ(std::string("42"), buffer); + wchar_t wbuffer[32] = {0}; + const auto wprepared = fmt::prepare(L"4{}"); + wprepared.format_to(wbuffer, 2); + EXPECT_EQ(std::wstring(L"42"), wbuffer); +} + +TEST(PrepareTest, FormatToIterator) { + std::string s(2, ' '); + const auto prepared = fmt::prepare("4{}"); + prepared.format_to(s.begin(), 2); + EXPECT_EQ("42", s); + std::wstring ws(2, L' '); + const auto wprepared = fmt::prepare(L"4{}"); + wprepared.format_to(ws.begin(), 2); + EXPECT_EQ(L"42", ws); +} + +TEST(PrepareTest, FormatToBackInserter) { + std::string s; + const auto prepared = fmt::prepare("4{}"); + prepared.format_to(std::back_inserter(s), 2); + EXPECT_EQ("42", s); + std::wstring ws; + const auto wprepared = fmt::prepare(L"4{}"); + wprepared.format_to(std::back_inserter(ws), 2); + EXPECT_EQ(L"42", ws); +} + +TEST(PrepareTest, FormatToBasicMemoryBuffer) { + fmt::basic_memory_buffer buffer; + const auto prepared = fmt::prepare("4{}"); + prepared.format_to(buffer, 2); + EXPECT_EQ("42", to_string(buffer)); + fmt::basic_memory_buffer wbuffer; + const auto wprepared = fmt::prepare(L"4{}"); + wprepared.format_to(wbuffer, 2); + EXPECT_EQ(L"42", to_string(wbuffer)); +} + +TEST(PrepareTest, FormatToMemoryBuffer) { + fmt::memory_buffer buffer; + const auto prepared = fmt::prepare("4{}"); + prepared.format_to(buffer, 2); + EXPECT_EQ("42", to_string(buffer)); + fmt::wmemory_buffer wbuffer; + const auto wprepared = fmt::prepare(L"4{}"); + wprepared.format_to(wbuffer, 2); + EXPECT_EQ(L"42", to_string(wbuffer)); +} From d7d2bebf99a723a369af8cc3edce4a376425b7cd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 08:29:37 -0700 Subject: [PATCH 2/9] Remove redundant typename --- include/fmt/core.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 4389ee1693a3..b9091fa6297e 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1138,10 +1138,9 @@ template class basic_format_args { bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; } - typename internal::type type(unsigned index) const { + internal::type type(unsigned index) const { unsigned shift = index * 4; - return static_cast((types_ & (0xfull << shift)) >> - shift); + return static_cast((types_ & (0xfull << shift)) >> shift); } friend class internal::arg_map; From 39f522a13a2def21afc0c2931aaa9e5caf3381b0 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 18:50:14 -0700 Subject: [PATCH 3/9] get_types -> encode_types --- include/fmt/core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index b9091fa6297e..ececfc9aa6ce 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -989,12 +989,12 @@ class locale_ref { template Locale get() const; }; -template constexpr unsigned long long get_types() { return 0; } +template constexpr unsigned long long encode_types() { return 0; } template -constexpr unsigned long long get_types() { +constexpr unsigned long long encode_types() { return mapped_type_constant::value | - (get_types() << 4); + (encode_types() << 4); } template @@ -1095,7 +1095,7 @@ template class format_arg_store { public: static constexpr unsigned long long TYPES = - is_packed ? internal::get_types() + is_packed ? internal::encode_types() : internal::is_unpacked_bit | num_args; format_arg_store(const Args&... args) From 32544b61084c7bf6f44741978118403d85cef41d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 19:22:19 -0700 Subject: [PATCH 4/9] More cleanup --- include/fmt/core.h | 20 +++++++------------- include/fmt/format.h | 7 +++++++ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index ececfc9aa6ce..443e918db540 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1094,9 +1094,10 @@ template class format_arg_store { friend class basic_format_args; public: - static constexpr unsigned long long TYPES = + static constexpr unsigned long long types = is_packed ? internal::encode_types() : internal::is_unpacked_bit | num_args; + FMT_DEPRECATED static constexpr unsigned long long TYPES = types; format_arg_store(const Args&... args) : data_{internal::make_arg(args)...} {} @@ -1173,7 +1174,7 @@ template class basic_format_args { */ template basic_format_args(const format_arg_store& store) - : types_(static_cast(store.TYPES)) { + : types_(static_cast(store.types)) { set_data(store.data_); } @@ -1206,23 +1207,16 @@ template class basic_format_args { // It is a separate type rather than an alias to make symbols readable. struct format_args : basic_format_args { template - format_args(Args&&... arg) - : basic_format_args(std::forward(arg)...) {} + format_args(Args&&... args) + : basic_format_args(std::forward(args)...) {} }; struct wformat_args : basic_format_args { template - wformat_args(Args&&... arg) - : basic_format_args(std::forward(arg)...) {} + wformat_args(Args&&... args) + : basic_format_args(std::forward(args)...) {} }; namespace internal { -template -FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) { - auto arg = ctx.arg(id); - if (!arg) ctx.on_error("argument index out of range"); - return arg; -} - template struct named_arg_base { basic_string_view name; diff --git a/include/fmt/format.h b/include/fmt/format.h index 1c257b1debde..f3bd1111cbd3 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1737,6 +1737,13 @@ FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) { struct auto_id {}; +template +FMT_CONSTEXPR typename Context::format_arg get_arg(Context& ctx, unsigned id) { + auto arg = ctx.arg(id); + if (!arg) ctx.on_error("argument index out of range"); + return arg; +} + // The standard format specifier handler with checking. template class specs_handler : public specs_setter { From d384cdd3977bab67bf1e36cc8431a2e1dd3d24df Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 20:16:57 -0700 Subject: [PATCH 5/9] Make is_contiguous_back_insert_iterator internal --- include/fmt/core.h | 35 +++++++++++++++++++---------------- include/fmt/format.h | 5 +++-- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 443e918db540..339a273b78a1 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1216,7 +1216,24 @@ struct wformat_args : basic_format_args { : basic_format_args(std::forward(args)...) {} }; +template struct is_contiguous : std::false_type {}; + +template +struct is_contiguous> : std::true_type {}; + +template +struct is_contiguous> : std::true_type {}; + namespace internal { + +// Workaround for a SFINAE issue in gcc < 9: +// https://stackoverflow.com/q/56436995/471164 +template +struct is_contiguous_back_insert_iterator : std::false_type {}; +template +struct is_contiguous_back_insert_iterator> + : is_contiguous {}; + template struct named_arg_base { basic_string_view name; @@ -1286,26 +1303,12 @@ inline internal::named_arg arg(const S& name, const T& arg) { template void arg(S, internal::named_arg) = delete; -template struct is_contiguous : std::false_type {}; - -template -struct is_contiguous> : std::true_type {}; - -template -struct is_contiguous> : std::true_type {}; - -template -struct is_contiguous_back_insert_iterator : std::false_type {}; - -template -struct is_contiguous_back_insert_iterator> - : is_contiguous {}; - /** Formats a string and writes the output to ``out``. */ // GCC 8 and earlier cannot handle std::back_insert_iterator with // vformat_to(...) overload, so SFINAE on iterator type instead. template , - FMT_ENABLE_IF(is_contiguous_back_insert_iterator::value)> + FMT_ENABLE_IF( + internal::is_contiguous_back_insert_iterator::value)> OutputIt vformat_to(OutputIt out, const S& format_str, basic_format_args> args) { using container = typename std::remove_reference::value && - !is_contiguous_back_insert_iterator::value)> + FMT_ENABLE_IF( + internal::is_output_iterator::value && + !internal::is_contiguous_back_insert_iterator::value)> inline OutputIt vformat_to( OutputIt out, const S& format_str, typename format_args_t>::type args) { From 4a502d980252590c7fa96a0025e8e470b6cd07e4 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 20:28:05 -0700 Subject: [PATCH 6/9] Add remove_reference_t --- include/fmt/core.h | 5 +++-- include/fmt/format.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 339a273b78a1..c2a7e30786c9 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -196,6 +196,8 @@ using enable_if_t = typename std::enable_if::type; template using conditional_t = typename std::conditional::type; template using bool_constant = std::integral_constant; +template +using remove_reference_t = typename std::remove_reference::type; struct monostate {}; @@ -1311,8 +1313,7 @@ template , internal::is_contiguous_back_insert_iterator::value)> OutputIt vformat_to(OutputIt out, const S& format_str, basic_format_args> args) { - using container = typename std::remove_reference::type; + using container = remove_reference_t; internal::container_buffer buf((internal::get_container(out))); internal::vformat_to(buf, to_string_view(format_str), args); return out; diff --git a/include/fmt/format.h b/include/fmt/format.h index 4e428af1ed9a..28bbe0f4e704 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3460,7 +3460,7 @@ template class is_output_iterator { template static const char& test(...); typedef decltype(test(typename it_category::type{})) type; - typedef typename std::remove_reference::type result; + typedef remove_reference_t result; public: static const bool value = !std::is_const::value; From 5e293bd97a3c67590b31c8113bb2746738517cea Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 20:36:39 -0700 Subject: [PATCH 7/9] Remove unnecessary qualification --- include/fmt/core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index c2a7e30786c9..0cbee42df872 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -716,7 +716,7 @@ template class value { custom.format = format_custom_arg< T, conditional_t::value, typename Context::template formatter_type, - internal::fallback_formatter>>; + fallback_formatter>>; } value(const named_arg_base& val) { named_arg = &val; } @@ -781,7 +781,7 @@ template struct arg_mapper { template , T>::value && - !internal::is_string::value)> + !is_string::value)> FMT_CONSTEXPR basic_string_view map(const T& val) { return basic_string_view(val); } @@ -1266,7 +1266,7 @@ void check_format_string(S); template > inline format_arg_store, Args...> make_args_checked( const S& format_str, const Args&... args) { - internal::check_format_string(format_str); + check_format_string(format_str); return {args...}; } @@ -1276,7 +1276,7 @@ std::basic_string vformat(basic_string_view format_str, template typename buffer_context::iterator vformat_to( - internal::buffer& buf, basic_string_view format_str, + buffer& buf, basic_string_view format_str, basic_format_args> args); } // namespace internal From 9d7b64a2597a9adcdb79978e09f1042711163a4c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Jun 2019 22:11:47 -0700 Subject: [PATCH 8/9] More cleanup --- include/fmt/core.h | 2 -- include/fmt/format.h | 9 +++------ include/fmt/printf.h | 5 +---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0cbee42df872..d5551f8363d6 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1228,8 +1228,6 @@ struct is_contiguous> : std::true_type {}; namespace internal { -// Workaround for a SFINAE issue in gcc < 9: -// https://stackoverflow.com/q/56436995/471164 template struct is_contiguous_back_insert_iterator : std::false_type {}; template diff --git a/include/fmt/format.h b/include/fmt/format.h index 28bbe0f4e704..a0fb6d74ca67 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -37,6 +37,8 @@ #include #include +#include "core.h" + #ifdef __clang__ # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else @@ -57,8 +59,6 @@ # define FMT_CUDA_VERSION 0 #endif -#include "core.h" - #if FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION # pragma GCC diagnostic push @@ -2270,10 +2270,7 @@ void handle_dynamic_spec(Spec& value, arg_ref ref, /** The default argument formatter. */ template -class arg_formatter - : public internal::function< - typename internal::arg_formatter_base::iterator>, - public internal::arg_formatter_base { +class arg_formatter : public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; typedef internal::arg_formatter_base base; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 0828c321f21b..c3a48ed4794e 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -204,10 +204,7 @@ template class basic_printf_context; \endrst */ template -class printf_arg_formatter - : public internal::function< - typename internal::arg_formatter_base::iterator>, - public internal::arg_formatter_base { +class printf_arg_formatter : public internal::arg_formatter_base { public: typedef decltype(std::declval().begin()) iterator; From e5422db4b2a19357ef21f3cab957d02ea5305682 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 12 Jun 2019 08:10:15 -0700 Subject: [PATCH 9/9] Fix handling of uintptr_t --- include/fmt/format-inl.h | 2 +- include/fmt/format.h | 35 +++++++++-------------------------- test/format-impl-test.cc | 2 +- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 8778337eba82..9a00c21f618b 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -236,7 +236,7 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str, namespace internal { -template <> FMT_FUNC int count_digits<4>(internal::uintptr_t n) { +template <> FMT_FUNC int count_digits<4>(internal::uintptr n) { // Assume little endian; pointer formatting is implementation-defined anyway. int i = static_cast(sizeof(void*)) - 1; while (i > 0 && n.value[i] == 0) --i; diff --git a/include/fmt/format.h b/include/fmt/format.h index a0fb6d74ca67..420cf6525974 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -243,13 +243,14 @@ namespace internal { #endif // A fallback implementation of uintptr_t for systems that lack it. -namespace uintptr { -struct uintptr_t { +struct uintptr { unsigned char value[sizeof(void*)]; }; -} // namespace uintptr -using uintptr::uintptr_t; -typedef std::numeric_limits numutil; +#ifdef UINTPTR_MAX +using uintptr_t = ::uintptr_t; +#else +using uintptr_t = uintptr; +#endif template inline bool use_grisu() { return FMT_USE_GRISU && std::numeric_limits::is_iec559 && @@ -303,25 +304,7 @@ typename Allocator::value_type* allocate(Allocator& alloc, std::size_t n) { #endif } } // namespace internal -FMT_END_NAMESPACE - -namespace std { -using namespace fmt::v5::internal::uintptr; -// Standard permits specialization of std::numeric_limits. This specialization -// is used to detect presence of uintptr_t. -template <> -class numeric_limits - : public std::numeric_limits { - public: - typedef uintptr_t uintptr_type; - static uintptr_type to_uint(const void* p) { - return fmt::internal::bit_cast(p); - } -}; -} // namespace std - -FMT_BEGIN_NAMESPACE template class basic_writer; template @@ -751,7 +734,7 @@ template inline int count_digits(UInt n) { return num_digits; } -template <> int count_digits<4>(internal::uintptr_t n); +template <> int count_digits<4>(internal::uintptr n); template inline size_t count_code_points(basic_string_view s) { @@ -980,7 +963,7 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits, } template -Char* format_uint(Char* buffer, internal::uintptr_t n, int num_digits, +Char* format_uint(Char* buffer, internal::uintptr n, int num_digits, bool = false) { auto char_digits = std::numeric_limits::digits / 4; int start = (num_digits + char_digits - 1) / char_digits - 1; @@ -1411,7 +1394,7 @@ class arg_formatter_base { } void write_pointer(const void* p) { - writer_.write_pointer(internal::numutil::to_uint(p), specs_); + writer_.write_pointer(internal::bit_cast(p), specs_); } protected: diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 25b5aeb0ae26..94bc5eeafbbf 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -257,7 +257,7 @@ TEST(UtilTest, CountDigits) { TEST(UtilTest, WriteUIntPtr) { fmt::memory_buffer buf; fmt::writer writer(buf); - writer.write_pointer(fmt::internal::bit_cast( + writer.write_pointer(fmt::internal::bit_cast( reinterpret_cast(0xface)), nullptr); EXPECT_EQ("0xface", to_string(buf));