diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 0813bc6db558..46d51f8e1925 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -63,8 +63,7 @@ inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { } FMT_BEGIN_NAMESPACE - -namespace { +namespace internal { #ifndef _MSC_VER # define FMT_SNPRINTF snprintf @@ -79,12 +78,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) { # define FMT_SNPRINTF fmt_snprintf #endif // _MSC_VER -#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) -# define FMT_SWPRINTF snwprintf -#else -# define FMT_SWPRINTF swprintf -#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) - typedef void (*FormatFunc)(internal::buffer&, int, string_view); // Portable thread-safe version of strerror. @@ -198,7 +191,7 @@ void report_error(FormatFunc func, int error_code, fwrite_fully(full_message.data(), 1, full_message.size(), stderr); std::fputc('\n', stderr); } -} // namespace +} // namespace internal #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) namespace internal { @@ -236,7 +229,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 n) { +template <> FMT_FUNC int count_digits<4>(internal::fallback_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; @@ -247,8 +240,10 @@ template <> FMT_FUNC int count_digits<4>(internal::uintptr n) { template int format_float(char* buf, std::size_t size, const char* format, int precision, T value) { - return precision < 0 ? FMT_SNPRINTF(buf, size, format, value) - : FMT_SNPRINTF(buf, size, format, precision, value); + // Suppress the warning about nonliteral format string. + auto snprintf_ptr = FMT_SNPRINTF; + return precision < 0 ? snprintf_ptr(buf, size, format, value) + : snprintf_ptr(buf, size, format, precision, value); } template @@ -925,7 +920,8 @@ FMT_FUNC void format_system_error(internal::buffer& out, int error_code, buf.resize(inline_buffer_size); for (;;) { char* system_message = &buf[0]; - int result = safe_strerror(error_code, system_message, buf.size()); + int result = + internal::safe_strerror(error_code, system_message, buf.size()); if (result == 0) { writer w(out); w.write(message); @@ -962,13 +958,13 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { memory_buffer buffer; internal::vformat_to(buffer, format_str, basic_format_args>(args)); - fwrite_fully(buffer.data(), 1, buffer.size(), f); + internal::fwrite_fully(buffer.data(), 1, buffer.size(), f); } FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) { wmemory_buffer buffer; internal::vformat_to(buffer, format_str, args); - fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f); + internal::fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f); } FMT_FUNC void vprint(string_view format_str, format_args args) { diff --git a/include/fmt/format.h b/include/fmt/format.h index 36498224454f..9ce1d30ce0d8 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -60,20 +60,17 @@ # define FMT_CUDA_VERSION 0 #endif -#if FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION -# pragma GCC diagnostic push +#ifdef __GNUC_LIBSTD__ +# define FMT_GNUC_LIBSTD_VERSION \ + (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif -// Disable warning about not handling all enums in switch statement even with -// a default case -# pragma GCC diagnostic ignored "-Wswitch-enum" +#if FMT_GCC_VERSION || FMT_CLANG_VERSION +# pragma GCC diagnostic push // Disable the warning about declaration shadowing because it affects too // many valid cases. # pragma GCC diagnostic ignored "-Wshadow" - -// Disable the warning about nonliteral format strings because we construct -// them dynamically when falling back to snprintf for FP formatting. -# pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif #if FMT_CLANG_VERSION @@ -101,11 +98,6 @@ # define FMT_HAS_BUILTIN(x) 0 #endif -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION \ - (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) -#endif - #ifndef FMT_THROW # if FMT_EXCEPTIONS # if FMT_MSC_VER @@ -128,13 +120,12 @@ FMT_END_NAMESPACE do { \ static_cast(sizeof(x)); \ assert(false); \ - } while (false); + } while (false) # endif #endif #ifndef FMT_USE_USER_DEFINED_LITERALS -// For Intel's compiler and NVIDIA's compiler both it and the system gcc/msc -// must support UDLs. +// For Intel and NVIDIA compilers both they and the system gcc/msc support UDLs. # if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \ FMT_MSC_VER >= 1900) && \ (!(FMT_ICC_VERSION || FMT_CUDA_VERSION) || FMT_ICC_VERSION >= 1500 || \ @@ -170,11 +161,11 @@ FMT_END_NAMESPACE // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #ifndef _MSC_VER -# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# if FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz) # define FMT_BUILTIN_CLZ(n) __builtin_clz(n) # endif -# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# if FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll) # define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) # endif #endif @@ -240,13 +231,13 @@ namespace internal { #endif // A fallback implementation of uintptr_t for systems that lack it. -struct uintptr { +struct fallback_uintptr { unsigned char value[sizeof(void*)]; }; #ifdef UINTPTR_MAX using uintptr_t = ::uintptr_t; #else -using uintptr_t = uintptr; +using uintptr_t = fallback_uintptr; #endif template inline bool use_grisu() { @@ -268,19 +259,8 @@ inline Dest bit_cast(const Source& source) { // An implementation of iterator_t for pre-C++20 systems. template using iterator_t = decltype(std::begin(std::declval())); - -template -typename Allocator::value_type* allocate(Allocator& alloc, std::size_t n) { -#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 - return std::allocator_traits::allocate(alloc, n); -#else - return alloc.allocate(n); -#endif -} } // namespace internal -template class basic_writer; - template class output_range { private: @@ -302,17 +282,18 @@ class output_range { template class back_insert_range : public output_range> { - typedef output_range> base; + using base = output_range>; public: - typedef typename Container::value_type value_type; + using value_type = typename Container::value_type; back_insert_range(Container& c) : base(std::back_inserter(c)) {} back_insert_range(typename base::iterator it) : base(it) {} }; -typedef basic_writer>> writer; -typedef basic_writer>> wwriter; +template class basic_writer; +using writer = basic_writer>>; +using wwriter = basic_writer>>; /** A formatting error such as invalid format string. */ class format_error : public std::runtime_error { @@ -485,7 +466,7 @@ void basic_memory_buffer::grow(std::size_t size) { std::size_t new_capacity = old_capacity + old_capacity / 2; if (size > new_capacity) new_capacity = size; T* old_data = this->data(); - T* new_data = internal::allocate(*this, new_capacity); + T* new_data = std::allocator_traits::allocate(*this, new_capacity); // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(old_data, old_data + this->size(), internal::make_checked(new_data, new_capacity)); @@ -713,7 +694,7 @@ template inline int count_digits(UInt n) { return num_digits; } -template <> int count_digits<4>(internal::uintptr n); +template <> int count_digits<4>(internal::fallback_uintptr n); template inline size_t count_code_points(basic_string_view s) { @@ -942,7 +923,7 @@ inline Char* format_uint(Char* buffer, UInt value, int num_digits, } template -Char* format_uint(Char* buffer, internal::uintptr n, int num_digits, +Char* format_uint(Char* buffer, internal::fallback_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; @@ -1326,27 +1307,14 @@ void arg_map::init(const basic_format_args& args) { if (args.is_packed()) { for (unsigned i = 0; /*nothing*/; ++i) { internal::type arg_type = args.type(i); - switch (arg_type) { - case internal::none_type: - return; - case internal::named_arg_type: - push_back(args.values_[i]); - break; - default: - break; // Do nothing. - } + if (arg_type == internal::none_type) return; + if (arg_type == internal::named_arg_type) push_back(args.values_[i]); } } for (unsigned i = 0;; ++i) { - switch (args.args_[i].type_) { - case internal::none_type: - return; - case internal::named_arg_type: - push_back(args.args_[i].value_); - break; - default: - break; // Do nothing. - } + auto type = args.args_[i].type_; + if (type == internal::none_type) return; + if (type == internal::named_arg_type) push_back(args.args_[i].value_); } } @@ -2139,10 +2107,14 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view format_str, template FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs( ParseContext& ctx) { - // GCC 7.2 requires initializer. - typedef typename ParseContext::char_type char_type; - conditional_t>::value, - formatter, + using char_type = typename ParseContext::char_type; + using context = buffer_context; + using mapped_type = + conditional_t::value != + internal::custom_type, + decltype(arg_mapper().map(std::declval())), T>; + conditional_t::value, + formatter, internal::fallback_formatter> f; return f.parse(ctx); @@ -3323,14 +3295,14 @@ arg_join join(It begin, It end, wstring_view sep) { \endrst */ template -arg_join, char> join( - const Range& range, string_view sep) { +arg_join, 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, wchar_t> join(const Range& range, + wstring_view sep) { return join(std::begin(range), std::end(range), sep); } #endif diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 2b0067b0f850..5bde66947bcc 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -171,9 +171,9 @@ TEST(FormatTest, FormatNegativeNaN) { TEST(FormatTest, StrError) { char* message = nullptr; char buffer[BUFFER_SIZE]; - EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = nullptr, 0), + EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0), "invalid buffer"); - EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0), + EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0), "invalid buffer"); buffer[0] = 'x'; #if defined(_GNU_SOURCE) && !defined(__COVERITY__) @@ -184,7 +184,8 @@ TEST(FormatTest, StrError) { int error_code = EDOM; #endif - int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE); + int result = + fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE); EXPECT_EQ(result, 0); std::size_t message_size = std::strlen(message); EXPECT_GE(BUFFER_SIZE - 1u, message_size); @@ -192,9 +193,10 @@ TEST(FormatTest, StrError) { // safe_strerror never uses buffer on MinGW. #ifndef __MINGW32__ - result = fmt::safe_strerror(error_code, message = buffer, message_size); + result = + fmt::internal::safe_strerror(error_code, message = buffer, message_size); EXPECT_EQ(ERANGE, result); - result = fmt::safe_strerror(error_code, message = buffer, 1); + result = fmt::internal::safe_strerror(error_code, message = buffer, 1); EXPECT_EQ(buffer, message); // Message should point to buffer. EXPECT_EQ(ERANGE, result); EXPECT_STREQ("", message); @@ -206,14 +208,14 @@ TEST(FormatTest, FormatErrorCode) { { fmt::memory_buffer buffer; format_to(buffer, "garbage"); - fmt::format_error_code(buffer, 42, "test"); + fmt::internal::format_error_code(buffer, 42, "test"); EXPECT_EQ("test: " + msg, to_string(buffer)); } { fmt::memory_buffer buffer; std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); - fmt::format_error_code(buffer, 42, prefix); + fmt::internal::format_error_code(buffer, 42, prefix); EXPECT_EQ(msg, to_string(buffer)); } int codes[] = {42, -1}; @@ -222,14 +224,14 @@ TEST(FormatTest, FormatErrorCode) { msg = fmt::format("error {}", codes[i]); fmt::memory_buffer buffer; std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x'); - fmt::format_error_code(buffer, codes[i], prefix); + fmt::internal::format_error_code(buffer, codes[i], prefix); EXPECT_EQ(prefix + sep + msg, to_string(buffer)); std::size_t size = fmt::inline_buffer_size; EXPECT_EQ(size, buffer.size()); buffer.resize(0); // Test with a message that doesn't fit into the buffer. prefix += 'x'; - fmt::format_error_code(buffer, codes[i], prefix); + fmt::internal::format_error_code(buffer, codes[i], prefix); EXPECT_EQ(msg, to_string(buffer)); } } @@ -257,7 +259,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)); diff --git a/test/format-test.cc b/test/format-test.cc index 6c05854cd289..5d6df9a87213 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1836,6 +1836,15 @@ TEST(FormatTest, UnpackedArgs) { 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g')); } +struct string_like {}; +fmt::string_view to_string_view(string_like) { return "foo"; } + +TEST(FormatTest, CompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); +} + #if FMT_USE_USER_DEFINED_LITERALS // Passing user-defined literals directly to EXPECT_EQ causes problems // with macro argument stringification (#) on some versions of GCC. @@ -1867,8 +1876,6 @@ TEST(LiteralsTest, NamedArg) { TEST(FormatTest, UdlTemplate) { EXPECT_EQ("foo", "foo"_format()); EXPECT_EQ(" 42", "{0:10}"_format(42)); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); - EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); } #endif // FMT_USE_USER_DEFINED_LITERALS diff --git a/test/mock-allocator.h b/test/mock-allocator.h index 41e2e270babb..1dd2dadbb319 100644 --- a/test/mock-allocator.h +++ b/test/mock-allocator.h @@ -52,7 +52,7 @@ template class allocator_ref { Allocator* get() const { return alloc_; } value_type* allocate(std::size_t n) { - return fmt::internal::allocate(*alloc_, n); + return std::allocator_traits::allocate(*alloc_, n); } void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); } };