From 6b0082e6c799c42c41596c008b2891fb6b786143 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 7 Nov 2023 09:24:33 -1000 Subject: Improve OpenBSD workaround --- include/fmt/os.h | 1 + src/os.cc | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/fmt/os.h b/include/fmt/os.h index 2b517cd4..462ac44d 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -48,6 +48,7 @@ // Calls to system functions are wrapped in FMT_SYSTEM for testability. #ifdef FMT_SYSTEM +# define FMT_HAS_SYSTEM # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) #else # define FMT_SYSTEM(call) ::call diff --git a/src/os.cc b/src/os.cc index bca410e9..a639e78c 100644 --- a/src/os.cc +++ b/src/os.cc @@ -18,8 +18,8 @@ # include # include -# ifdef _WRS_KERNEL // VxWorks7 kernel -# include // getpagesize +# ifdef _WRS_KERNEL // VxWorks7 kernel +# include // getpagesize # endif # ifndef _WIN32 @@ -182,10 +182,14 @@ void buffered_file::close() { } int buffered_file::descriptor() const { -#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. - int fd = fileno(file_); -#else +#if !defined(fileno) int fd = FMT_POSIX_CALL(fileno(file_)); +#elif defined(FMT_HAS_SYSTEM) + // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. +# define FMT_DISABLE_MACRO + int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_)); +#else + int fd = fileno(file_); #endif if (fd == -1) FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor"))); -- cgit v1.2.3 From cbb18c237a41b703d1b92a1c76aa8d2148165e52 Mon Sep 17 00:00:00 2001 From: Luis Caro Campos <3535649+jcar87@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:34:42 +0000 Subject: Add support for CMake 3.28 C++ modules (#3679) --- CMakeLists.txt | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 485eb86c..9605cd59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8...3.26) +cmake_minimum_required(VERSION 3.8...3.28) # Fallback for using newer policies on CMake <3.12. if(${CMAKE_VERSION} VERSION_LESS 3.12) @@ -26,7 +26,7 @@ endfunction() # DEPRECATED! Should be merged into add_module_library. function(enable_module target) - if (MSVC) + if (MSVC AND CMAKE_VERSION VERSION_LESS 3.28) set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) target_compile_options(${target} PRIVATE /interface /ifcOutput ${BMI} @@ -65,7 +65,7 @@ function(add_module_library name) # `std` is affected by CMake options and may be higher than C++20. get_target_property(std ${name} CXX_STANDARD) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_VERSION VERSION_LESS 3.28) set(pcms) foreach (src ${sources}) get_filename_component(pcm ${src} NAME_WE) @@ -103,7 +103,11 @@ function(add_module_library name) DEPENDS ${pcm}) endforeach () endif () - target_sources(${name} PRIVATE ${sources}) + if(CMAKE_VERSION VERSION_LESS 3.28) + target_sources(${name} PRIVATE ${sources}) + else() + target_sources(${name} PUBLIC FILE_SET fmt_module TYPE CXX_MODULES FILES ${sources}) + endif() endfunction() include(CMakeParseArguments) @@ -392,8 +396,14 @@ if (FMT_INSTALL) LIBRARY DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR} PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" + FILE_SET fmt_module DESTINATION "${FMT_LIB_DIR}/cxx/miu" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + if(FMT_MODULE AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) + #Install format.cc and os.cc which are #included by the fmt.cc module interface file + install(FILES src/format.cc src/os.cc DESTINATION "${FMT_LIB_DIR}/cxx/miu/src") + endif() + # Use a namespace because CMake provides better diagnostics for namespaced # imported targets. export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: -- cgit v1.2.3 From ec628561c2f7938a4bf9af40116eabe23c098ff3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 13 Nov 2023 06:41:28 -1000 Subject: Fix formatting --- CMakeLists.txt | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9605cd59..e6467f6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.8...3.28) # Fallback for using newer policies on CMake <3.12. -if(${CMAKE_VERSION} VERSION_LESS 3.12) +if (${CMAKE_VERSION} VERSION_LESS 3.12) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() +endif () # Determine if fmt is built as a subproject (using add_subdirectory) # or if it is the master project. @@ -103,11 +103,12 @@ function(add_module_library name) DEPENDS ${pcm}) endforeach () endif () - if(CMAKE_VERSION VERSION_LESS 3.28) + if (CMAKE_VERSION VERSION_LESS 3.28) target_sources(${name} PRIVATE ${sources}) - else() - target_sources(${name} PUBLIC FILE_SET fmt_module TYPE CXX_MODULES FILES ${sources}) - endif() + else () + target_sources(${name} PUBLIC FILE_SET fmt_module TYPE CXX_MODULES + FILES ${sources}) + endif () endfunction() include(CMakeParseArguments) @@ -166,10 +167,10 @@ set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") if (FMT_SYSTEM_HEADERS) set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) endif () -if(CMAKE_SYSTEM_NAME STREQUAL "MSDOS") +if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS") set(FMT_TEST OFF) message(STATUS "MSDOS is incompatible with gtest") -endif() +endif () # Get version from core.h file(READ include/fmt/core.h core_h) @@ -338,7 +339,7 @@ if (BUILD_SHARED_LIBS) endif () if (FMT_SAFE_DURATION_CAST) target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) -endif() +endif () add_library(fmt-header-only INTERFACE) add_library(fmt::fmt-header-only ALIAS fmt-header-only) @@ -346,7 +347,8 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only) target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) target_compile_features(fmt-header-only INTERFACE cxx_std_11) -target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE +target_include_directories(fmt-header-only + ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE $ $) @@ -399,10 +401,12 @@ if (FMT_INSTALL) FILE_SET fmt_module DESTINATION "${FMT_LIB_DIR}/cxx/miu" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - if(FMT_MODULE AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) - #Install format.cc and os.cc which are #included by the fmt.cc module interface file - install(FILES src/format.cc src/os.cc DESTINATION "${FMT_LIB_DIR}/cxx/miu/src") - endif() + if (FMT_MODULE AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) + # Install format.cc and os.cc which are included by the fmt.cc module + # interface file. + install(FILES src/format.cc src/os.cc + DESTINATION "${FMT_LIB_DIR}/cxx/miu/src") + endif () # Use a namespace because CMake provides better diagnostics for namespaced # imported targets. -- cgit v1.2.3 From 045b05d79e8c827ea815d765e62d92b879184b41 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 14 Nov 2023 06:48:16 -1000 Subject: Revert CMake changes --- CMakeLists.txt | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e6467f6e..639c44f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.8...3.28) +cmake_minimum_required(VERSION 3.8...3.26) # Fallback for using newer policies on CMake <3.12. if (${CMAKE_VERSION} VERSION_LESS 3.12) @@ -26,7 +26,7 @@ endfunction() # DEPRECATED! Should be merged into add_module_library. function(enable_module target) - if (MSVC AND CMAKE_VERSION VERSION_LESS 3.28) + if (MSVC) set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) target_compile_options(${target} PRIVATE /interface /ifcOutput ${BMI} @@ -65,7 +65,7 @@ function(add_module_library name) # `std` is affected by CMake options and may be higher than C++20. get_target_property(std ${name} CXX_STANDARD) - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_VERSION VERSION_LESS 3.28) + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(pcms) foreach (src ${sources}) get_filename_component(pcm ${src} NAME_WE) @@ -103,12 +103,7 @@ function(add_module_library name) DEPENDS ${pcm}) endforeach () endif () - if (CMAKE_VERSION VERSION_LESS 3.28) - target_sources(${name} PRIVATE ${sources}) - else () - target_sources(${name} PUBLIC FILE_SET fmt_module TYPE CXX_MODULES - FILES ${sources}) - endif () + target_sources(${name} PRIVATE ${sources}) endfunction() include(CMakeParseArguments) @@ -398,16 +393,8 @@ if (FMT_INSTALL) LIBRARY DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR} PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" - FILE_SET fmt_module DESTINATION "${FMT_LIB_DIR}/cxx/miu" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - if (FMT_MODULE AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28) - # Install format.cc and os.cc which are included by the fmt.cc module - # interface file. - install(FILES src/format.cc src/os.cc - DESTINATION "${FMT_LIB_DIR}/cxx/miu/src") - endif () - # Use a namespace because CMake provides better diagnostics for namespaced # imported targets. export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: -- cgit v1.2.3 From 45e124ee431ee931dea30ce20cc97d2f8fc33049 Mon Sep 17 00:00:00 2001 From: Kai Aoki Date: Thu, 16 Nov 2023 02:19:08 +0900 Subject: Added workaround for old xcode compiler bug (#3716) * Added macros to disable std::filesystem and std::variant as a workaround for old xcode bugs * Change macro to positive logic --- include/fmt/std.h | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 4d1f97d2..4799df1a 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -59,7 +59,24 @@ # endif #endif -#ifdef __cpp_lib_filesystem +// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. +#ifndef FMT_CPP_LIB_FILESYSTEM +# ifdef __cpp_lib_filesystem +# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem +# else +# define FMT_CPP_LIB_FILESYSTEM 0 +# endif +#endif + +#ifndef FMT_CPP_LIB_VARIANT +# ifdef __cpp_lib_variant +# define FMT_CPP_LIB_VARIANT __cpp_lib_variant +# else +# define FMT_CPP_LIB_VARIANT 0 +# endif +#endif + +#if FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE namespace detail { @@ -133,7 +150,7 @@ template struct formatter { } }; FMT_END_NAMESPACE -#endif +#endif // FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE FMT_EXPORT @@ -211,7 +228,7 @@ struct formatter, Char, FMT_END_NAMESPACE #endif // __cpp_lib_optional -#ifdef __cpp_lib_variant +#if FMT_CPP_LIB_VARIANT FMT_BEGIN_NAMESPACE namespace detail { @@ -302,7 +319,7 @@ struct formatter< } }; FMT_END_NAMESPACE -#endif // __cpp_lib_variant +#endif // FMT_CPP_LIB_VARIANT FMT_BEGIN_NAMESPACE FMT_EXPORT -- cgit v1.2.3 From 649fe0fc8b9366375eab67639cab404617c527cd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 14 Nov 2023 07:48:10 -1000 Subject: Fix handling of null strings with the s specifier --- include/fmt/format.h | 7 ++++--- test/format-test.cc | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 4104d91f..b84b6a5c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2342,9 +2342,10 @@ template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { - return specs.type != presentation_type::pointer - ? write(out, basic_string_view(s), specs, {}) - : write_ptr(out, bit_cast(s), &specs); + if (specs.type == presentation_type::pointer) + return write_ptr(out, bit_cast(s), &specs); + if (!s) throw_format_error("string pointer is null"); + return write(out, basic_string_view(s), specs, {}); } template (nullptr); EXPECT_THROW_MSG( - (void)fmt::format(runtime("{0}"), static_cast(nullptr)), + (void)fmt::format("{}", nullstr), + format_error, "string pointer is null"); + EXPECT_THROW_MSG( + (void)fmt::format("{:s}", nullstr), format_error, "string pointer is null"); } -- cgit v1.2.3 From 864a8b5f38d1742da51a1f1dc827bb4c55c97a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20B=C3=B8e?= <94781160+cptFracassa@users.noreply.github.com> Date: Fri, 17 Nov 2023 00:02:33 +0100 Subject: fix: support optional with format_as(T) (#3713) Formatting a std::optional where T had a custom format_as(T) function failed to compile with clang, due to set_debug_format being hidden by private inheritance. This fix makes the function available through a using clause. --- include/fmt/format.h | 1 + test/std-test.cc | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/include/fmt/format.h b/include/fmt/format.h index b84b6a5c..60595b2f 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4054,6 +4054,7 @@ struct formatter::value>> : private formatter, Char> { using base = formatter, Char>; using base::parse; + using base::set_debug_format; template auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { diff --git a/test/std-test.cc b/test/std-test.cc index 41183dbf..dc1073b5 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -90,6 +90,36 @@ TEST(std_test, optional) { #endif } +namespace my_nso { +enum class my_number { + one, + two, +}; +auto format_as(my_number number) -> fmt::string_view { + return number == my_number::one ? "first" : "second"; +} + +class my_class { + public: + int av; + + private: + friend auto format_as(const my_class& elm) -> std::string { + return fmt::to_string(elm.av); + } +}; +} // namespace my_nso +TEST(std_test, optional_format_as) { +#ifdef __cpp_lib_optional + EXPECT_EQ(fmt::format("{}", std::optional{}), "none"); + EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_number::one}), + "optional(\"first\")"); + EXPECT_EQ(fmt::format("{}", std::optional{}), "none"); + EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}), + "optional(\"7\")"); +#endif +} + struct throws_on_move { throws_on_move() = default; -- cgit v1.2.3 From c13753a70cc55f3b1c99fb8f8395e78e5f9cae43 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 18 Nov 2023 07:54:36 -1000 Subject: Fix handling of invalid Unicode in precision --- include/fmt/format.h | 23 +++++++++++++++-------- test/format-test.cc | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 60595b2f..d4d20965 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -791,12 +791,18 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { // Calculates the index of the nth code point in a UTF-8 string. inline auto code_point_index(string_view s, size_t n) -> size_t { - const char* data = s.data(); - size_t num_code_points = 0; - for (size_t i = 0, size = s.size(); i != size; ++i) { - if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i; - } - return s.size(); + size_t result = s.size(); + const char* begin = s.begin(); + for_each_codepoint( + s, [begin, &n, &result](uint32_t, string_view sv) { + if (n != 0) { + --n; + return true; + } + result = to_unsigned(sv.begin() - begin); + return false; + }); + return result; } inline auto code_point_index(basic_string_view s, size_t n) @@ -1962,8 +1968,9 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt { *out++ = static_cast('\''); if ((needs_escape(static_cast(v)) && v != static_cast('"')) || v == static_cast('\'')) { - out = write_escaped_cp( - out, find_escape_result{v_array, v_array + 1, static_cast(v)}); + out = write_escaped_cp(out, + find_escape_result{v_array, v_array + 1, + static_cast(v)}); } else { *out++ = v; } diff --git a/test/format-test.cc b/test/format-test.cc index f4e7eeca..08b4e8c1 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1050,6 +1050,7 @@ TEST(format_test, precision) { EXPECT_EQ("st", fmt::format("{0:.2}", "str")); EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі")); + EXPECT_EQ("123456", fmt::format("{0:.6}", "123456\xad")); } TEST(format_test, runtime_precision) { -- cgit v1.2.3 From dd6f657a79104101a2e4ea6ba90f69e0dc114822 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Wed, 22 Nov 2023 09:46:04 -0800 Subject: Remove this-> from decltype (#3723) * Remove this-> from decltype The latest version of MSVC doesn't like it, and removing it doesn't seem to harm anything. * Add ifdef for GCC < 5 --- include/fmt/core.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 18ebada2..aaeb2cfa 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -244,6 +244,15 @@ # endif #endif +// GCC < 5 requires this-> in decltype +#ifndef FMT_DECLTYPE_THIS +# if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 +# define FMT_DECLTYPE_THIS this-> +# else +# define FMT_DECLTYPE_THIS +# endif +#endif + // Enable minimal optimizations for more compact code in debug mode. FMT_GCC_PRAGMA("GCC push_options") #if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ @@ -1439,7 +1448,7 @@ template struct arg_mapper { // Only map owning types because mapping views can be unsafe. template , FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(FMT_DECLTYPE_THIS map(U())) { return map(format_as(val)); } @@ -1463,13 +1472,13 @@ template struct arg_mapper { !is_string::value && !is_char::value && !is_named_arg::value && !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) { + FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(FMT_DECLTYPE_THIS do_map(val)) { return do_map(val); } template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) - -> decltype(this->map(named_arg.value)) { + -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) { return map(named_arg.value); } -- cgit v1.2.3 From 8a39388516cf3630b0498fce373de0dfd54bdcac Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 24 Nov 2023 09:23:58 -0800 Subject: Fix the default locale in ostream_formatter --- include/fmt/ostream.h | 7 +++---- test/ostream-test.cc | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 782ace5c..2ed35df1 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -91,12 +91,11 @@ void write_buffer(std::basic_ostream& os, buffer& buf) { } template -void format_value(buffer& buf, const T& value, - locale_ref loc = locale_ref()) { +void format_value(buffer& buf, const T& value) { auto&& format_buf = formatbuf>(buf); auto&& output = std::basic_ostream(&format_buf); #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) - if (loc) output.imbue(loc.get()); + output.imbue(std::locale::classic()); // The default is always unlocalized. #endif output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); @@ -117,7 +116,7 @@ struct basic_ostream_formatter : formatter, Char> { auto format(const T& value, basic_format_context& ctx) const -> OutputIt { auto buffer = basic_memory_buffer(); - detail::format_value(buffer, value, ctx.locale()); + detail::format_value(buffer, value); return formatter, Char>::format( {buffer.data(), buffer.size()}, ctx); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index b2d15466..98ee0757 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -17,7 +17,7 @@ struct test {}; // included after fmt/format.h. namespace fmt { template <> struct formatter : formatter { - auto format(const test&, format_context& ctx) -> decltype(ctx.out()) { + auto format(const test&, format_context& ctx) const -> decltype(ctx.out()) { return formatter::format(42, ctx); } }; @@ -289,3 +289,20 @@ TEST(ostream_test, closed_ofstream) { std::ofstream ofs; fmt::print(ofs, "discard"); } + +struct unlocalized {}; + +auto operator<<(std::ostream& os, unlocalized) + -> std::ostream& { + return os << 12345; +} + +namespace fmt { +template <> struct formatter : ostream_formatter {}; +} // namespace fmt + +TEST(ostream_test, unlocalized) { + auto loc = get_locale("en_US.UTF-8"); + std::locale::global(loc); + EXPECT_EQ(fmt::format(loc, "{}", unlocalized()), "12345"); +} -- cgit v1.2.3 From bea7ecc7107514f29eb6b0cef665173cf8a3d18b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 24 Nov 2023 09:45:56 -0800 Subject: Disable locale-specific tests on OpenBSD --- test/util.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/util.cc b/test/util.cc index 4ff34a91..d3f2dc73 100644 --- a/test/util.cc +++ b/test/util.cc @@ -39,6 +39,11 @@ std::locale get_locale(const char* name, const char* alt_name) { auto loc = do_get_locale(name); if (loc == std::locale::classic() && alt_name) loc = do_get_locale(alt_name); +#ifdef __OpenBSD__ + // Locales are not working in OpenBSD: + // https://github.com/fmtlib/fmt/issues/3670. + loc = std::locale::classic(); +#endif if (loc == std::locale::classic()) fmt::print(stderr, "{} locale is missing.\n", name); return loc; -- cgit v1.2.3 From ffa5b14fe3c05fb111c6e1c4f9b98a7f5e0cc8ee Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 24 Nov 2023 10:09:21 -0800 Subject: Make gtest-extra-test more portable --- test/gtest-extra-test.cc | 2 +- test/gtest-extra.cc | 4 ++-- test/gtest-extra.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/gtest-extra-test.cc b/test/gtest-extra-test.cc index 42340a2d..34054b68 100644 --- a/test/gtest-extra-test.cc +++ b/test/gtest-extra-test.cc @@ -354,7 +354,7 @@ TEST(output_redirect_test, dup_error_in_ctor) { FMT_POSIX(close(fd)); std::unique_ptr redir{nullptr}; EXPECT_SYSTEM_ERROR_NOASSERT( - redir.reset(new output_redirect(f.get())), EBADF, + redir.reset(new output_redirect(f.get(), false)), EBADF, fmt::format("cannot duplicate file descriptor {}", fd)); copy.dup2(fd); // "undo" close or dtor will fail } diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc index 542e4b5e..3d27cf96 100644 --- a/test/gtest-extra.cc +++ b/test/gtest-extra.cc @@ -11,8 +11,8 @@ using fmt::file; -output_redirect::output_redirect(FILE* f) : file_(f) { - flush(); +output_redirect::output_redirect(FILE* f, bool flush) : file_(f) { + if (flush) this->flush(); int fd = FMT_POSIX(fileno(f)); // Create a file object referring to the original file. original_ = file::dup(fd); diff --git a/test/gtest-extra.h b/test/gtest-extra.h index 03a07a2a..e08c94c0 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -77,7 +77,7 @@ class output_redirect { void restore(); public: - explicit output_redirect(FILE* file); + explicit output_redirect(FILE* file, bool flush = true); ~output_redirect() noexcept; output_redirect(const output_redirect&) = delete; -- cgit v1.2.3 From 06f1c0d725855861535e9e65cd4d502aca7c61ed Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 24 Nov 2023 10:21:57 -0800 Subject: Clarify that calling non-const format is deprecated --- include/fmt/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fmt/core.h b/include/fmt/core.h index aaeb2cfa..ba982964 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1318,6 +1318,7 @@ template class value { parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t(), const T, T>; + // Calling format through a mutable reference is deprecated. ctx.advance_to(f.format(*static_cast(arg), ctx)); } }; -- cgit v1.2.3 From c3f9a73445d1569966894ed47061ae54f849188f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 07:41:04 -0800 Subject: Apply coding conventions --- test/scan-test.cc | 4 ++-- test/scan.h | 44 ++++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index bec54134..596579c6 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -70,7 +70,7 @@ namespace fmt { template <> struct scanner { std::string format; - scan_parse_context::iterator parse(scan_parse_context& ctx) { + auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator { auto it = ctx.begin(); if (it != ctx.end() && *it == ':') ++it; auto end = it; @@ -82,7 +82,7 @@ template <> struct scanner { } template - typename ScanContext::iterator scan(tm& t, ScanContext& ctx) { + auto scan(tm& t, ScanContext& ctx) const -> typename ScanContext::iterator { auto result = strptime(ctx.begin(), format.c_str(), &t); if (!result) throw format_error("failed to parse time"); return result; diff --git a/test/scan.h b/test/scan.h index a2cb2aa6..d6fd6770 100644 --- a/test/scan.h +++ b/test/scan.h @@ -27,8 +27,8 @@ class scan_parse_context { explicit FMT_CONSTEXPR scan_parse_context(string_view format) : format_(format) {} - FMT_CONSTEXPR iterator begin() const { return format_.begin(); } - FMT_CONSTEXPR iterator end() const { return format_.end(); } + FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); } + FMT_CONSTEXPR auto end() const -> iterator { return format_.end(); } void advance_to(iterator it) { format_.remove_prefix(detail::to_unsigned(it - begin())); @@ -44,8 +44,8 @@ struct scan_context { explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {} - iterator begin() const { return input_.data(); } - iterator end() const { return begin() + input_.size(); } + auto begin() const -> iterator { return input_.data(); } + auto end() const -> iterator { return begin() + input_.size(); } void advance_to(iterator it) { input_.remove_prefix(detail::to_unsigned(it - begin())); @@ -106,7 +106,7 @@ class scan_arg { template static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx, scan_context& ctx) { - scanner s; + auto s = scanner(); parse_ctx.advance_to(s.parse(parse_ctx)); ctx.advance_to(s.scan(*static_cast(arg), ctx)); } @@ -134,7 +134,7 @@ struct scan_handler : error_handler { int next_arg_id_; scan_arg arg_; - template T read_uint() { + template auto read_uint() -> T { T value = 0; auto it = scan_ctx_.begin(), end = scan_ctx_.end(); while (it != end) { @@ -147,7 +147,7 @@ struct scan_handler : error_handler { return value; } - template T read_int() { + template auto read_int() -> T { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); bool negative = it != end && *it == '-'; if (negative) ++it; @@ -162,7 +162,7 @@ struct scan_handler : error_handler { scan_args args) : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {} - const char* pos() const { return scan_ctx_.begin(); } + auto pos() const -> const char* { return scan_ctx_.begin(); } void on_text(const char* begin, const char* end) { auto size = to_unsigned(end - begin); @@ -172,13 +172,13 @@ struct scan_handler : error_handler { scan_ctx_.advance_to(it + size); } - FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); } - FMT_CONSTEXPR int on_arg_id(int id) { + FMT_CONSTEXPR auto on_arg_id() -> int { return on_arg_id(next_arg_id_++); } + FMT_CONSTEXPR auto on_arg_id(int id) -> int { if (id >= args_.size) on_error("argument index out of range"); arg_ = args_.data[id]; return id; } - FMT_CONSTEXPR int on_arg_id(string_view id) { + FMT_CONSTEXPR auto on_arg_id(string_view id) -> int { if (id.data()) on_error("invalid format"); return 0; } @@ -215,7 +215,7 @@ struct scan_handler : error_handler { } } - const char* on_format_specs(int, const char* begin, const char*) { + auto on_format_specs(int, const char* begin, const char*) -> const char* { if (arg_.type != scan_type::custom_type) return begin; parse_ctx_.advance_to(begin); arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); @@ -224,21 +224,21 @@ struct scan_handler : error_handler { }; } // namespace detail -template -std::array make_scan_args(Args&... args) { +template +auto make_scan_args(T&... args) -> std::array { return {{args...}}; } -string_view::iterator vscan(string_view input, string_view format_str, - scan_args args) { - detail::scan_handler h(format_str, input, args); - detail::parse_format_string(format_str, h); +auto vscan(string_view input, string_view fmt, scan_args args) + -> string_view::iterator { + auto h = detail::scan_handler(fmt, input, args); + detail::parse_format_string(fmt, h); return input.begin() + (h.pos() - &*input.begin()); } -template -string_view::iterator scan(string_view input, string_view format_str, - Args&... args) { - return vscan(input, format_str, make_scan_args(args...)); +template +auto scan(string_view input, string_view fmt, T&... args) + -> string_view::iterator { + return vscan(input, fmt, make_scan_args(args...)); } FMT_END_NAMESPACE -- cgit v1.2.3 From c4283ec471bd3efdb114bc1ab30c7c7c5e5e0ee0 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 07:58:27 -0800 Subject: Fix a libc++ warning and move the test to the right place --- test/format-impl-test.cc | 7 ------- test/format-test.cc | 10 ++++++++++ test/xchar-test.cc | 12 ------------ 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 4d6198b6..eda1f239 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -246,13 +246,6 @@ TEST(format_impl_test, format_error_code) { } } -TEST(format_impl_test, compute_width) { - EXPECT_EQ(4, - fmt::detail::compute_width( - fmt::basic_string_view( - reinterpret_cast("ёжик")))); -} - // Tests fmt::detail::count_digits for integer type Int. template void test_count_digits() { for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i)); diff --git a/test/format-test.cc b/test/format-test.cc index 08b4e8c1..34eb28a3 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -173,6 +173,10 @@ TEST(util_test, parse_nonnegative_int) { EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1); } +TEST(format_impl_test, compute_width) { + EXPECT_EQ(fmt::detail::compute_width("вожык"), 5); +} + TEST(util_test, utf8_to_utf16) { auto u = fmt::detail::utf8_to_utf16("лошадка"); EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str()); @@ -1053,6 +1057,12 @@ TEST(format_test, precision) { EXPECT_EQ("123456", fmt::format("{0:.6}", "123456\xad")); } +TEST(xchar_test, utf8_precision) { + auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés + EXPECT_EQ(fmt::detail::compute_width(result), 4); + EXPECT_EQ(result, "caf\u00e9"); +} + TEST(format_test, runtime_precision) { char format_str[buffer_size]; safe_sprintf(format_str, "{0:.{%u", UINT_MAX); diff --git a/test/xchar-test.cc b/test/xchar-test.cc index f72e94dc..90ada586 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -187,18 +187,6 @@ template std::string from_u8str(const S& str) { return std::string(str.begin(), str.end()); } -TEST(xchar_test, format_utf8_precision) { - using str_type = std::basic_string; - auto format = - str_type(reinterpret_cast(u8"{:.4}")); - auto str = str_type(reinterpret_cast( - u8"caf\u00e9s")); // cafés - auto result = fmt::format(format, str); - EXPECT_EQ(fmt::detail::compute_width(result), 4); - EXPECT_EQ(result.size(), 5); - EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5))); -} - TEST(xchar_test, format_to) { auto buf = std::vector(); fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0'); -- cgit v1.2.3 From ccc9ab7bf9c5aab0071708a0f65e3019bd96b8fe Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 08:23:41 -0800 Subject: Include correct header --- test/scan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/scan.h b/test/scan.h index d6fd6770..a7b3b33c 100644 --- a/test/scan.h +++ b/test/scan.h @@ -9,7 +9,7 @@ #include #include -#include "fmt/format.h" +#include "fmt/core.h" FMT_BEGIN_NAMESPACE template struct scanner { -- cgit v1.2.3 From 7f8d4191157025813f5fd520cb68738b3ee0fe69 Mon Sep 17 00:00:00 2001 From: Corentin Schreiber Date: Sat, 25 Nov 2023 16:36:55 +0000 Subject: Fix overflow in time_point formatting with large dates (#3727) * Fix #3725 and rename fmt_safe_duration_cast to fmt_duration_cast The function is now more generic and will handle all casts. It also takes care of toggling safe vs unsafe casts using FMT_SAFE_DURATION_CAST. * Refactor fmt_duration_cast to put #ifdef inside the function * Fix compilation error with FMT_USE_LOCAL_TIME --- include/fmt/chrono.h | 113 ++++++++++++++++++++++++++++++--------------------- test/chrono-test.cc | 14 +++++++ 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 7bd4206d..3d2007bb 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -430,6 +430,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } +template +struct is_same_arithmetic_type + : public std::integral_constant::value && + std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)> { +}; + +template < + typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(is_same_arithmetic_type::value)> +To fmt_duration_cast(std::chrono::duration from) { +#if FMT_SAFE_DURATION_CAST + // throwing version of safe_duration_cast + // only available for integer<->integer or float<->float casts + int ec; + To to = safe_duration_cast::safe_duration_cast(from, ec); + if (ec) FMT_THROW(format_error("cannot format duration")); + return to; +#else + // standard duration cast, may overflow and invoke undefined behavior + return std::chrono::duration_cast(from); +#endif +} + +template < + typename To, typename FromRep, typename FromPeriod, + FMT_ENABLE_IF(!is_same_arithmetic_type::value)> +To fmt_duration_cast(std::chrono::duration from) { + // mixed integer<->float cast is not supported with safe_duration_cast + // fallback to standard duration cast in this case + return std::chrono::duration_cast(from); +} + +template +std::time_t to_time_t( + std::chrono::time_point time_point) { + // cannot use std::chrono::system_clock::to_time_t() since this would first + // require a cast to std::chrono::system_clock::time_point, which could + // overflow. + return fmt_duration_cast>( + time_point.time_since_epoch()) + .count(); +} } // namespace detail FMT_BEGIN_EXPORT @@ -478,8 +523,8 @@ inline std::tm localtime(std::time_t time) { #if FMT_USE_LOCAL_TIME template inline auto localtime(std::chrono::local_time time) -> std::tm { - return localtime(std::chrono::system_clock::to_time_t( - std::chrono::current_zone()->to_sys(time))); + return localtime( + detail::to_time_t(std::chrono::current_zone()->to_sys(time))); } #endif @@ -523,9 +568,10 @@ inline std::tm gmtime(std::time_t time) { return gt.tm_; } +template inline std::tm gmtime( - std::chrono::time_point time_point) { - return gmtime(std::chrono::system_clock::to_time_t(time_point)); + std::chrono::time_point time_point) { + return gmtime(detail::to_time_t(time_point)); } namespace detail { @@ -1051,13 +1097,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { std::chrono::seconds::rep>::type, std::ratio<1, detail::pow10(num_fractional_digits)>>; - const auto fractional = - d - std::chrono::duration_cast(d); + const auto fractional = d - fmt_duration_cast(d); const auto subseconds = std::chrono::treat_as_floating_point< typename subsecond_precision::rep>::value ? fractional.count() - : std::chrono::duration_cast(fractional).count(); + : fmt_duration_cast(fractional).count(); auto n = static_cast>(subseconds); const int num_digits = detail::count_digits(n); @@ -1620,17 +1665,6 @@ template struct make_unsigned_or_unchanged { using type = typename std::make_unsigned::type; }; -#if FMT_SAFE_DURATION_CAST -// throwing version of safe_duration_cast -template -To fmt_safe_duration_cast(std::chrono::duration from) { - int ec; - To to = safe_duration_cast::safe_duration_cast(from, ec); - if (ec) FMT_THROW(format_error("cannot format duration")); - return to; -} -#endif - template ::value)> inline std::chrono::duration get_milliseconds( @@ -1640,17 +1674,17 @@ inline std::chrono::duration get_milliseconds( #if FMT_SAFE_DURATION_CAST using CommonSecondsType = typename std::common_type::type; - const auto d_as_common = fmt_safe_duration_cast(d); + const auto d_as_common = fmt_duration_cast(d); const auto d_as_whole_seconds = - fmt_safe_duration_cast(d_as_common); + fmt_duration_cast(d_as_common); // this conversion should be nonproblematic const auto diff = d_as_common - d_as_whole_seconds; const auto ms = - fmt_safe_duration_cast>(diff); + fmt_duration_cast>(diff); return ms; #else - auto s = std::chrono::duration_cast(d); - return std::chrono::duration_cast(d - s); + auto s = fmt_duration_cast(d); + return fmt_duration_cast(d - s); #endif } @@ -1751,14 +1785,8 @@ struct chrono_formatter { // this may overflow and/or the result may not fit in the // target type. -#if FMT_SAFE_DURATION_CAST // might need checked conversion (rep!=Rep) - auto tmpval = std::chrono::duration(val); - s = fmt_safe_duration_cast(tmpval); -#else - s = std::chrono::duration_cast( - std::chrono::duration(val)); -#endif + s = fmt_duration_cast(std::chrono::duration(val)); } // returns true if nan or inf, writes to out. @@ -2082,25 +2110,22 @@ struct formatter, period::num != 1 || period::den != 1 || std::is_floating_point::value)) { const auto epoch = val.time_since_epoch(); - auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); + auto subsecs = detail::fmt_duration_cast( + epoch - detail::fmt_duration_cast(epoch)); if (subsecs.count() < 0) { auto second = - std::chrono::duration_cast(std::chrono::seconds(1)); + detail::fmt_duration_cast(std::chrono::seconds(1)); if (epoch.count() < ((Duration::min)() + second).count()) FMT_THROW(format_error("duration is too small")); subsecs += second; val -= second; } - return formatter::do_format( - gmtime(std::chrono::time_point_cast(val)), ctx, - &subsecs); + return formatter::do_format(gmtime(val), ctx, &subsecs); } - return formatter::format( - gmtime(std::chrono::time_point_cast(val)), ctx); + return formatter::format(gmtime(val), ctx); } }; @@ -2119,17 +2144,13 @@ struct formatter, Char> if (period::num != 1 || period::den != 1 || std::is_floating_point::value) { const auto epoch = val.time_since_epoch(); - const auto subsecs = std::chrono::duration_cast( - epoch - std::chrono::duration_cast(epoch)); + const auto subsecs = detail::fmt_duration_cast( + epoch - detail::fmt_duration_cast(epoch)); - return formatter::do_format( - localtime(std::chrono::time_point_cast(val)), - ctx, &subsecs); + return formatter::do_format(localtime(val), ctx, &subsecs); } - return formatter::format( - localtime(std::chrono::time_point_cast(val)), - ctx); + return formatter::format(localtime(val), ctx); } }; #endif diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 07760688..b562a50e 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -874,6 +874,20 @@ TEST(chrono_test, timestamps_ratios) { t4(std::chrono::duration>(1)); EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03"); + + std::chrono::time_point + t5(std::chrono::seconds(32503680000)); + + EXPECT_EQ(fmt::format("{:%Y-%m-%d}", t5), "3000-01-01"); + +#if FMT_SAFE_DURATION_CAST + using years = std::chrono::duration>; + std::chrono::time_point t6( + (years(std::numeric_limits::max()))); + + EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", t6), fmt::format_error, + "cannot format duration"); +#endif } TEST(chrono_test, timestamps_sub_seconds) { -- cgit v1.2.3 From 2d1e4bb35e93f050d4cebc3ef6f5559ae448f4ea Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 09:05:20 -0800 Subject: Remove a useless comment --- include/fmt/format.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index d4d20965..8762826e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1028,7 +1028,6 @@ class basic_memory_buffer final : public detail::buffer { /** Increases the buffer capacity to *new_capacity*. */ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } - // Directly append data into the buffer using detail::buffer::append; template void append(const ContiguousRange& range) { -- cgit v1.2.3 From 6988be3878661db9c1809e319adae1fd743a1835 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 09:11:53 -0800 Subject: Bump version --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index ba982964..3c7fd048 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -18,7 +18,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100101 +#define FMT_VERSION 100102 #if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) -- cgit v1.2.3 From 73fae91e644215d4f7e09a9e62a14595539f4486 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 25 Nov 2023 09:45:38 -0800 Subject: Cleanup .gitignore --- .gitignore | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 8a37cb98..1406ac34 100644 --- a/.gitignore +++ b/.gitignore @@ -1,37 +1,24 @@ +*.a +*.so* +*.xcodeproj +*~ .vscode/ -.vs/ - -*.iml -.idea/ -.externalNativeBuild/ -.gradle/ -gradle/ -gradlew* -local.properties -build/ -support/.cxx - -bin/ -/_CPack_Packages /CMakeScripts +/Testing +/_CPack_Packages /doc/doxyxml /doc/html /doc/node_modules -virtualenv -/Testing /install_manifest.txt -*~ -*.a -*.so* -*.xcodeproj -*.zip -cmake_install.cmake -CPack*.cmake -fmt-*.cmake -CTestTestfile.cmake CMakeCache.txt CMakeFiles +CPack*.cmake +CTestTestfile.cmake FMT.build Makefile -run-msbuild.bat +bin/ +build/ +cmake_install.cmake +fmt-*.cmake fmt.pc +virtualenv -- cgit v1.2.3 From 5cfd28d476c6859617878f951931b8ce7d36b9df Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 26 Nov 2023 09:22:31 -0800 Subject: Experiment with FILE in scan --- test/scan-test.cc | 14 +++++++ test/scan.h | 118 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 14 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 596579c6..ada5f698 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -6,6 +6,7 @@ // For the license information refer to format.h. #include "scan.h" +#include "fmt/os.h" #include @@ -114,3 +115,16 @@ TEST(scan_test, example) { EXPECT_EQ(key, "answer"); EXPECT_EQ(value, 42); } + +#if FMT_USE_FCNTL +TEST(scan_test, file) { + fmt::file read_end, write_end; + fmt::file::pipe(read_end, write_end); + fmt::string_view input = "4"; + write_end.write(input.data(), input.size()); + write_end.close(); + int value = 0; + fmt::scan(read_end.fdopen("r").get(), "{}", value); + EXPECT_EQ(value, 4); +} +#endif // FMT_USE_FCNTL diff --git a/test/scan.h b/test/scan.h index a7b3b33c..96e46678 100644 --- a/test/scan.h +++ b/test/scan.h @@ -9,9 +9,92 @@ #include #include -#include "fmt/core.h" +#include "fmt/format.h" FMT_BEGIN_NAMESPACE +namespace detail { + +class scan_buffer { + private: + const char* ptr_; + size_t size_; + + protected: + scan_buffer(const char* ptr, size_t size) : ptr_(ptr), size_(size) {} + ~scan_buffer() = default; + + void set(const char* data, size_t size) noexcept { + ptr_ = data; + size_ = size; + } + + // Fills the buffer with more input. + virtual void fill() = 0; + + public: + scan_buffer(const scan_buffer&) = delete; + void operator=(const scan_buffer&) = delete; + + auto begin() noexcept -> const char* { return ptr_; } + auto end() noexcept -> const char* { return ptr_ + size_; } + + auto size() const -> size_t { return size_; } + + // Consume n code units from the buffer. + void consume(size_t n) { + FMT_ASSERT(n <= size_, ""); + ptr_ += n; + size_ -= n; + } +}; + +class string_scan_buffer : public scan_buffer { + private: + void fill() override {} + + public: + explicit string_scan_buffer(string_view s) : scan_buffer(s.data(), s.size()) { + } +}; + +class file_scan_buffer : public scan_buffer { + private: + FILE* file_; + char next_; + + template + void set_buffer(int, F* f) { + this->set(reinterpret_cast(f->_p), detail::to_unsigned(f->_r)); + } + void set_buffer(int c, ...) { + if (c == EOF) return; + next_ = static_cast(c); + this->set(&next_, 1); + } + + void fill() override { + int result = getc(file_); + if (result == EOF) { + if (ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); + return; + } + // Put the character back since we are only filling the buffer. + if (ungetc(result, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); + set_buffer(result, file_); + } + + public: + explicit file_scan_buffer(FILE* f) + : scan_buffer(nullptr, 0), file_(f) { + // TODO: lock file? + set_buffer(EOF, f); + if (size() == 0) fill(); + } +}; +} // namespace detail + template struct scanner { // A deleted default constructor indicates a disabled scanner. scanner() = delete; @@ -37,18 +120,19 @@ class scan_parse_context { struct scan_context { private: - string_view input_; + detail::scan_buffer& buf_; public: using iterator = const char*; - explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {} + explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf) : buf_(buf) {} - auto begin() const -> iterator { return input_.data(); } - auto end() const -> iterator { return begin() + input_.size(); } + // TODO: an iterator that automatically calls read on end of buffer + auto begin() const -> iterator { return buf_.begin(); } + auto end() const -> iterator { return buf_.end(); } void advance_to(iterator it) { - input_.remove_prefix(detail::to_unsigned(it - begin())); + buf_.consume(detail::to_unsigned(it - begin())); } }; @@ -158,9 +242,8 @@ struct scan_handler : error_handler { } public: - FMT_CONSTEXPR scan_handler(string_view format, string_view input, - scan_args args) - : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {} + FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf, scan_args args) + : parse_ctx_(format), scan_ctx_(buf), args_(args), next_arg_id_(0) {} auto pos() const -> const char* { return scan_ctx_.begin(); } @@ -229,16 +312,23 @@ auto make_scan_args(T&... args) -> std::array { return {{args...}}; } -auto vscan(string_view input, string_view fmt, scan_args args) - -> string_view::iterator { - auto h = detail::scan_handler(fmt, input, args); +void vscan(detail::scan_buffer& buf, string_view fmt, scan_args args) { + auto h = detail::scan_handler(fmt, buf, args); detail::parse_format_string(fmt, h); - return input.begin() + (h.pos() - &*input.begin()); } template auto scan(string_view input, string_view fmt, T&... args) -> string_view::iterator { - return vscan(input, fmt, make_scan_args(args...)); + auto&& buf = detail::string_scan_buffer(input); + vscan(buf, fmt, make_scan_args(args...)); + return input.begin() + (buf.begin() - input.data()); } + +template +void scan(std::FILE* f, string_view fmt, T&... args) { + auto&& buf = detail::file_scan_buffer(f); + vscan(buf, fmt, make_scan_args(args...)); +} + FMT_END_NAMESPACE -- cgit v1.2.3 From b87ea22e29913b5a71c02b772f303b9bf441c14b Mon Sep 17 00:00:00 2001 From: js324 Date: Wed, 29 Nov 2023 17:49:36 -0500 Subject: Add native and generic representation for filesystem::path format spec (#3729) --- include/fmt/std.h | 15 +++++++++++++-- test/std-test.cc | 9 +++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 4799df1a..7b92cea5 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -114,6 +114,7 @@ template struct formatter { format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; + char path_type_ = 'n'; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } @@ -130,20 +131,30 @@ template struct formatter { debug_ = true; ++it; } + if (it != end && (*it == 'g' || *it == 'n')) { + path_type_ = *it++; + } return it; } template auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; + auto path_type = path_type_; + # ifdef _WIN32 + auto path_string = path_type == 'n' ? p.native() : p.generic_wstring(); + # else + auto path_string = path_type == 'n' ? p.native() : p.generic_string(); + # endif + detail::handle_dynamic_spec(specs.width, width_ref_, ctx); if (!debug_) { - auto s = detail::get_path_string(p, p.native()); + auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); } auto quoted = basic_memory_buffer(); - detail::write_escaped_path(quoted, p, p.native()); + detail::write_escaped_path(quoted, p, path_string); return detail::write(ctx.out(), basic_string_view(quoted.data(), quoted.size()), specs); diff --git a/test/std-test.cc b/test/std-test.cc index dc1073b5..7a81aaf9 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -25,15 +25,20 @@ TEST(std_test, path) { EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar"); EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\""); + + EXPECT_EQ(fmt::format("{:n}", path("/usr/bin")), "/usr/bin"); + EXPECT_EQ(fmt::format("{:g}", path("/usr/bin")), "/usr/bin"); +# ifdef _WIN32 + EXPECT_EQ(fmt::format("{:n}", path("C:\\foo")), "C:\\foo"); + EXPECT_EQ(fmt::format("{:g}", path("C:\\foo")), "C:/foo"); -# ifdef _WIN32 EXPECT_EQ(fmt::format("{}", path( L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448" L"\x0447\x044B\x043D\x0430")), "Шчучыншчына"); EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "�"); EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\""); -# endif +# endif } // Test ambiguity problem described in #2954. -- cgit v1.2.3 From 04718008abd2d49456f2acfd6542f8b4cc873efe Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 30 Nov 2023 07:34:26 -0800 Subject: Minor cleanup --- include/fmt/chrono.h | 11 +++++------ include/fmt/std.h | 26 +++++++++++++------------- test/std-test.cc | 15 +++++++-------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 3d2007bb..9b4f9d4e 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -444,14 +444,14 @@ template < FMT_ENABLE_IF(is_same_arithmetic_type::value)> To fmt_duration_cast(std::chrono::duration from) { #if FMT_SAFE_DURATION_CAST - // throwing version of safe_duration_cast - // only available for integer<->integer or float<->float casts + // Throwing version of safe_duration_cast is only available for + // integer to integer or float to float casts. int ec; To to = safe_duration_cast::safe_duration_cast(from, ec); if (ec) FMT_THROW(format_error("cannot format duration")); return to; #else - // standard duration cast, may overflow and invoke undefined behavior + // Standard duration cast, may overflow. return std::chrono::duration_cast(from); #endif } @@ -460,15 +460,14 @@ template < typename To, typename FromRep, typename FromPeriod, FMT_ENABLE_IF(!is_same_arithmetic_type::value)> To fmt_duration_cast(std::chrono::duration from) { - // mixed integer<->float cast is not supported with safe_duration_cast - // fallback to standard duration cast in this case + // Mixed integer <-> float cast is not supported by safe_duration_cast. return std::chrono::duration_cast(from); } template std::time_t to_time_t( std::chrono::time_point time_point) { - // cannot use std::chrono::system_clock::to_time_t() since this would first + // Cannot use std::chrono::system_clock::to_time_t since this would first // require a cast to std::chrono::system_clock::time_point, which could // overflow. return fmt_duration_cast>( diff --git a/include/fmt/std.h b/include/fmt/std.h index 7b92cea5..61fe0cac 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -65,7 +65,7 @@ # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem # else # define FMT_CPP_LIB_FILESYSTEM 0 -# endif +# endif #endif #ifndef FMT_CPP_LIB_VARIANT @@ -81,8 +81,9 @@ FMT_BEGIN_NAMESPACE namespace detail { -template auto get_path_string( - const std::filesystem::path& p, const std::basic_string& native) { +template +auto get_path_string(const std::filesystem::path& p, + const std::basic_string& native) { if constexpr (std::is_same_v && std::is_same_v) return to_utf8(native, to_utf8_error_policy::replace); else @@ -93,7 +94,8 @@ template void write_escaped_path(basic_memory_buffer& quoted, const std::filesystem::path& p, const std::basic_string& native) { - if constexpr (std::is_same_v && std::is_same_v) { + if constexpr (std::is_same_v && + std::is_same_v) { auto buf = basic_memory_buffer(); write_escaped_string(std::back_inserter(buf), native); bool valid = to_utf8::convert(quoted, {buf.data(), buf.size()}); @@ -131,21 +133,19 @@ template struct formatter { debug_ = true; ++it; } - if (it != end && (*it == 'g' || *it == 'n')) { + if (it != end && (*it == 'g' || *it == 'n')) path_type_ = *it++; - } return it; } template auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; - auto path_type = path_type_; - # ifdef _WIN32 - auto path_string = path_type == 'n' ? p.native() : p.generic_wstring(); - # else - auto path_string = path_type == 'n' ? p.native() : p.generic_string(); - # endif +# ifdef _WIN32 + auto path_string = path_type_ == 'n' ? p.native() : p.generic_wstring(); +# else + auto path_string = path_type_ == 'n' ? p.native() : p.generic_string(); +# endif detail::handle_dynamic_spec(specs.width, width_ref_, ctx); @@ -161,7 +161,7 @@ template struct formatter { } }; FMT_END_NAMESPACE -#endif // FMT_CPP_LIB_FILESYSTEM +#endif // FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE FMT_EXPORT diff --git a/test/std-test.cc b/test/std-test.cc index 7a81aaf9..55a48810 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -25,20 +25,19 @@ TEST(std_test, path) { EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar"); EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\""); - + EXPECT_EQ(fmt::format("{:n}", path("/usr/bin")), "/usr/bin"); EXPECT_EQ(fmt::format("{:g}", path("/usr/bin")), "/usr/bin"); -# ifdef _WIN32 +# ifdef _WIN32 EXPECT_EQ(fmt::format("{:n}", path("C:\\foo")), "C:\\foo"); EXPECT_EQ(fmt::format("{:g}", path("C:\\foo")), "C:/foo"); - EXPECT_EQ(fmt::format("{}", path( - L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448" - L"\x0447\x044B\x043D\x0430")), + EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448" + L"\x0447\x044B\x043D\x0430")), "Шчучыншчына"); EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "�"); EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\""); -# endif +# endif } // Test ambiguity problem described in #2954. @@ -290,10 +289,10 @@ TEST(std_test, format_atomic) { #ifdef __cpp_lib_atomic_flag_test TEST(std_test, format_atomic_flag) { std::atomic_flag f = ATOMIC_FLAG_INIT; - (void) f.test_and_set(); + (void)f.test_and_set(); EXPECT_EQ(fmt::format("{}", f), "true"); const std::atomic_flag cf = ATOMIC_FLAG_INIT; EXPECT_EQ(fmt::format("{}", cf), "false"); } -#endif // __cpp_lib_atomic_flag_test +#endif // __cpp_lib_atomic_flag_test -- cgit v1.2.3 From 2a8a694466076eeb69ed5030545409d6bcaeb85b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 30 Nov 2023 08:20:01 -0800 Subject: Clarify that mixing code unit types is deprecated --- include/fmt/std.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 61fe0cac..dde1292f 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -354,7 +354,7 @@ template struct formatter { FMT_EXPORT template struct formatter< - T, Char, + T, Char, // DEPRECATED! Mixing code unit types. typename std::enable_if::value>::type> { private: bool with_typename_ = false; -- cgit v1.2.3 From 8f83ee2ad1ec5508434a4570063154465f31d492 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 30 Nov 2023 08:21:57 -0800 Subject: Apply coding conventions --- include/fmt/core.h | 6 ++++-- include/fmt/format.h | 25 ++++++++++++------------- include/fmt/std.h | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 3c7fd048..2fcaf845 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1449,7 +1449,8 @@ template struct arg_mapper { // Only map owning types because mapping views can be unsafe. template , FMT_ENABLE_IF(std::is_arithmetic::value)> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(FMT_DECLTYPE_THIS map(U())) { + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) + -> decltype(FMT_DECLTYPE_THIS map(U())) { return map(format_as(val)); } @@ -1473,7 +1474,8 @@ template struct arg_mapper { !is_string::value && !is_char::value && !is_named_arg::value && !std::is_arithmetic>::value)> - FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(FMT_DECLTYPE_THIS do_map(val)) { + FMT_CONSTEXPR FMT_INLINE auto map(T& val) + -> decltype(FMT_DECLTYPE_THIS do_map(val)) { return do_map(val); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 8762826e..79bf5256 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -793,15 +793,14 @@ inline auto code_point_index(basic_string_view s, size_t n) -> size_t { inline auto code_point_index(string_view s, size_t n) -> size_t { size_t result = s.size(); const char* begin = s.begin(); - for_each_codepoint( - s, [begin, &n, &result](uint32_t, string_view sv) { - if (n != 0) { - --n; - return true; - } - result = to_unsigned(sv.begin() - begin); - return false; - }); + for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { + if (n != 0) { + --n; + return true; + } + result = to_unsigned(sv.begin() - begin); + return false; + }); return result; } @@ -1162,10 +1161,10 @@ using uint32_or_64_or_128_t = template using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; -#define FMT_POWERS_OF_10(factor) \ - factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \ - (factor)*1000000, (factor)*10000000, (factor)*100000000, \ - (factor)*1000000000 +#define FMT_POWERS_OF_10(factor) \ + factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \ + (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \ + (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. constexpr const char* digits2(size_t value) { diff --git a/include/fmt/std.h b/include/fmt/std.h index dde1292f..86c8756f 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -225,7 +225,7 @@ struct formatter, Char, } template - auto format(std::optional const& opt, FormatContext& ctx) const + auto format(const std::optional& opt, FormatContext& ctx) const -> decltype(ctx.out()) { if (!opt) return detail::write(ctx.out(), none); -- cgit v1.2.3 From 99b9fbf8eff2fc63e639260bd96e22f058f4f818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix-Antoine=20Constantin?= <60141446+felix642@users.noreply.github.com> Date: Thu, 30 Nov 2023 13:05:56 -0500 Subject: Add formatter for std::source_location (#3730) --- include/fmt/std.h | 30 ++++++++++++++++++++++++++++++ test/std-test.cc | 10 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/fmt/std.h b/include/fmt/std.h index 86c8756f..6f6b23ff 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -38,6 +38,10 @@ # endif #endif +#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() +# include +#endif + // GCC 4 does not support FMT_HAS_INCLUDE. #if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) # include @@ -239,6 +243,32 @@ struct formatter, Char, FMT_END_NAMESPACE #endif // __cpp_lib_optional +#ifdef __cpp_lib_source_location +FMT_BEGIN_NAMESPACE +FMT_EXPORT +template<> +struct formatter { + template FMT_CONSTEXPR auto parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + auto format(const std::source_location& loc, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + out = detail::write(out, loc.file_name()); + out = detail::write(out, ':'); + out = detail::write(out, loc.line()); + out = detail::write(out, ':'); + out = detail::write(out, loc.column()); + out = detail::write(out, ": "); + out = detail::write(out, loc.function_name()); + return out; + } +}; +FMT_END_NAMESPACE +#endif + #if FMT_CPP_LIB_VARIANT FMT_BEGIN_NAMESPACE namespace detail { diff --git a/test/std-test.cc b/test/std-test.cc index 55a48810..2e8ff177 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -66,6 +66,16 @@ TEST(std_test, thread_id) { EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty()); } +#ifdef __cpp_lib_source_location +TEST(std_test, source_location) { + std::source_location loc = std::source_location::current(); + EXPECT_EQ(fmt::format("{}", loc), std::string(loc.file_name()) + ":" + + std::to_string(loc.line()) + ":" + + std::to_string(loc.column()) + ": " + + loc.function_name()); +} +#endif + TEST(std_test, optional) { #ifdef __cpp_lib_optional EXPECT_EQ(fmt::format("{}", std::optional{}), "none"); -- cgit v1.2.3 From f5750892436a667fe622e5ecc8a02c15a5d9bc88 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 1 Dec 2023 08:52:42 -0800 Subject: Simplify test --- test/std-test.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/std-test.cc b/test/std-test.cc index 2e8ff177..f288e144 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -69,10 +69,9 @@ TEST(std_test, thread_id) { #ifdef __cpp_lib_source_location TEST(std_test, source_location) { std::source_location loc = std::source_location::current(); - EXPECT_EQ(fmt::format("{}", loc), std::string(loc.file_name()) + ":" + - std::to_string(loc.line()) + ":" + - std::to_string(loc.column()) + ": " + - loc.function_name()); + EXPECT_EQ(fmt::format("{}", loc), + fmt::format("{}:{}:{}: {}", loc.file_name(), loc.line(), + loc.column(), loc.function_name())); } #endif -- cgit v1.2.3 From 71bd51e6c20d1674b67c3089fecfac98ce3f9169 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Sun, 3 Dec 2023 19:29:58 +0400 Subject: Implement `%j` specifier for `std::chrono::duration` (#3732) This adds support for `%j` presentation type for duration types: > "If the type being formatted is a specialization of duration, the decimal number of days without padding." Fixes #3643. --- include/fmt/chrono.h | 8 +++++++- test/chrono-test.cc | 12 +++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 9b4f9d4e..57cd0b70 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1622,6 +1622,7 @@ struct chrono_format_checker : null_chrono_spec_handler { template FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_day_of_year() {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -1806,6 +1807,7 @@ struct chrono_formatter { return true; } + Rep days() const { return static_cast(s.count() / 86400); } Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } Rep hour12() const { @@ -1884,10 +1886,14 @@ struct chrono_formatter { void on_dec0_week_of_year(numeric_system) {} void on_dec1_week_of_year(numeric_system) {} void on_iso_week_of_year(numeric_system) {} - void on_day_of_year() {} void on_day_of_month(numeric_system) {} void on_day_of_month_space(numeric_system) {} + void on_day_of_year() { + if (handle_nan_inf()) return; + write(days(), 0); + } + void on_24_hour(numeric_system ns, pad_type pad) { if (handle_nan_inf()) return; diff --git a/test/chrono-test.cc b/test/chrono-test.cc index b562a50e..cb672816 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -24,6 +24,12 @@ using testing::Contains; # define FMT_HAS_C99_STRFTIME 1 #endif +#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L +using days = std::chrono::days; +#else +using days = std::chrono::duration>; +#endif + auto make_tm() -> std::tm { auto time = std::tm(); time.tm_mday = 1; @@ -456,9 +462,7 @@ TEST(chrono_test, format_default) { fmt::format("{}", std::chrono::duration(42))); EXPECT_EQ("42min", fmt::format("{}", std::chrono::minutes(42))); EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); -# if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L - EXPECT_EQ("42d", fmt::format("{}", std::chrono::days(42))); -# endif + EXPECT_EQ("42d", fmt::format("{}", days(42))); EXPECT_EQ( "42[15]s", fmt::format("{}", std::chrono::duration>(42))); @@ -533,6 +537,8 @@ TEST(chrono_test, format_specs) { EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24))); EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4))); EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14))); + EXPECT_EQ("12345", fmt::format("{:%j}", days(12345))); + EXPECT_EQ("12345", fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12))); EXPECT_EQ("03:25:45", fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345))); -- cgit v1.2.3 From 5d55375a8a6aabf39528bdf48f7b3ded5ef4e9bb Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 2 Dec 2023 09:34:27 -0800 Subject: Experiment with scan buffering --- test/scan-test.cc | 26 +++++---- test/scan.h | 161 +++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 138 insertions(+), 49 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index ada5f698..25f76109 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -6,24 +6,24 @@ // For the license information refer to format.h. #include "scan.h" -#include "fmt/os.h" #include #include +#include "fmt/os.h" #include "gmock/gmock.h" #include "gtest-extra.h" TEST(scan_test, read_text) { - auto s = fmt::string_view("foo"); + fmt::string_view s = "foo"; auto end = fmt::scan(s, "foo"); EXPECT_EQ(end, s.end()); EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input"); } TEST(scan_test, read_int) { - auto n = int(); + int n = 0; fmt::scan("42", "{}", n); EXPECT_EQ(n, 42); fmt::scan("-42", "{}", n); @@ -39,7 +39,7 @@ TEST(scan_test, read_longlong) { } TEST(scan_test, read_uint) { - auto n = unsigned(); + unsigned n = 0; fmt::scan("42", "{}", n); EXPECT_EQ(n, 42); EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error, @@ -55,13 +55,13 @@ TEST(scan_test, read_ulonglong) { } TEST(scan_test, read_string) { - auto s = std::string(); + std::string s; fmt::scan("foo", "{}", s); EXPECT_EQ(s, "foo"); } TEST(scan_test, read_string_view) { - auto s = fmt::string_view(); + fmt::string_view s; fmt::scan("foo", "{}", s); EXPECT_EQ(s, "foo"); } @@ -83,21 +83,23 @@ template <> struct scanner { } template - auto scan(tm& t, ScanContext& ctx) const -> typename ScanContext::iterator { - auto result = strptime(ctx.begin(), format.c_str(), &t); - if (!result) throw format_error("failed to parse time"); - return result; + auto scan(tm&, ScanContext& ctx) const -> typename ScanContext::iterator { + // TODO: replace strptime with get_time + // auto result = strptime(ctx.begin(), format.c_str(), &t); + // if (!result) throw format_error("failed to parse time"); + // return result; + return ctx.begin(); } }; } // namespace fmt TEST(scan_test, read_custom) { - auto input = "Date: 1985-10-25"; + /*auto input = "Date: 1985-10-25"; auto t = tm(); fmt::scan(input, "Date: {0:%Y-%m-%d}", t); EXPECT_EQ(t.tm_year, 85); EXPECT_EQ(t.tm_mon, 9); - EXPECT_EQ(t.tm_mday, 25); + EXPECT_EQ(t.tm_mday, 25);*/ } #endif diff --git a/test/scan.h b/test/scan.h index 96e46678..6a44b646 100644 --- a/test/scan.h +++ b/test/scan.h @@ -14,37 +14,118 @@ FMT_BEGIN_NAMESPACE namespace detail { +struct maybe_contiguous_range { + const char* begin; + const char* end; + + explicit operator bool() const { return begin != nullptr; } +}; + class scan_buffer { private: const char* ptr_; - size_t size_; + const char* end_; + bool contiguous_; protected: - scan_buffer(const char* ptr, size_t size) : ptr_(ptr), size_(size) {} + scan_buffer(const char* ptr, const char* end, bool contiguous) + : ptr_(ptr), end_(end), contiguous_(contiguous) {} ~scan_buffer() = default; - void set(const char* data, size_t size) noexcept { - ptr_ = data; - size_ = size; + auto is_empty() const -> bool { return ptr_ == end_; } + + void set(const char* ptr, const char* end) noexcept { + ptr_ = ptr; + end_ = end; } - // Fills the buffer with more input. + auto peek() -> int { + if (ptr_ == end_) { + // TODO: refill buffer + return EOF; + } + return *ptr_; + } + + // Fills the buffer with more input if available. virtual void fill() = 0; public: scan_buffer(const scan_buffer&) = delete; void operator=(const scan_buffer&) = delete; - auto begin() noexcept -> const char* { return ptr_; } - auto end() noexcept -> const char* { return ptr_ + size_; } + class iterator { + private: + const char** ptr_; + scan_buffer* buf_; // This could be merged with ptr_. + char value_; + + static auto sentinel() -> const char** { + static const char* ptr = nullptr; + return &ptr; + } + + friend class scan_buffer; + + friend auto operator==(iterator lhs, iterator rhs) -> bool { + return *lhs.ptr_ == *rhs.ptr_; + } + friend auto operator!=(iterator lhs, iterator rhs) -> bool { + return *lhs.ptr_ != *rhs.ptr_; + } + + iterator(scan_buffer* buf) + : ptr_(&buf->ptr_), buf_(buf), value_(static_cast(buf->peek())) { + if (value_ == EOF) ptr_ = sentinel(); + } + + public: + iterator() : ptr_(sentinel()), buf_(nullptr) {} - auto size() const -> size_t { return size_; } + auto operator++() -> iterator& { + if (!buf_->try_consume()) ptr_ = sentinel(); + value_ = *buf_->ptr_; + return *this; + } + auto operator++(int) -> iterator { + iterator copy = *this; + ++*this; + return copy; + } + auto operator*() const -> char { return value_; } + + auto base() const -> const char* { return buf_->ptr_; } + + friend auto to_contiguous(iterator it) -> maybe_contiguous_range; + friend void advance(iterator& it, size_t n); + }; + + friend auto to_contiguous(iterator it) -> maybe_contiguous_range { + if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_}; + return {nullptr, nullptr}; + } + friend void advance(iterator& it, size_t n) { + FMT_ASSERT(it.buf_->is_contiguous(), ""); + const char*& ptr = it.buf_->ptr_; + ptr += n; + it.value_ = *ptr; + if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel(); + } - // Consume n code units from the buffer. - void consume(size_t n) { - FMT_ASSERT(n <= size_, ""); - ptr_ += n; - size_ -= n; + auto begin() noexcept -> iterator { return this; } + auto end() noexcept -> iterator { return {}; } + + auto is_contiguous() const -> bool { return contiguous_; } + + // Tries consuming a single code unit. + auto try_consume() -> bool { + FMT_ASSERT(ptr_ != end_, ""); + ++ptr_; + if (ptr_ == end_) { + // TODO: refill buffer + return false; + } + return true; } }; @@ -53,8 +134,8 @@ class string_scan_buffer : public scan_buffer { void fill() override {} public: - explicit string_scan_buffer(string_view s) : scan_buffer(s.data(), s.size()) { - } + explicit string_scan_buffer(string_view s) + : scan_buffer(s.begin(), s.end(), true) {} }; class file_scan_buffer : public scan_buffer { @@ -64,12 +145,13 @@ class file_scan_buffer : public scan_buffer { template void set_buffer(int, F* f) { - this->set(reinterpret_cast(f->_p), detail::to_unsigned(f->_r)); + const char* ptr = reinterpret_cast(f->_p); + this->set(ptr, ptr + f->_r); } void set_buffer(int c, ...) { if (c == EOF) return; next_ = static_cast(c); - this->set(&next_, 1); + this->set(&next_, &next_ + 1); } void fill() override { @@ -87,10 +169,10 @@ class file_scan_buffer : public scan_buffer { public: explicit file_scan_buffer(FILE* f) - : scan_buffer(nullptr, 0), file_(f) { + : scan_buffer(nullptr, nullptr, false), file_(f) { // TODO: lock file? set_buffer(EOF, f); - if (size() == 0) fill(); + if (is_empty()) fill(); } }; } // namespace detail @@ -123,16 +205,16 @@ struct scan_context { detail::scan_buffer& buf_; public: - using iterator = const char*; + using iterator = detail::scan_buffer::iterator; explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf) : buf_(buf) {} - // TODO: an iterator that automatically calls read on end of buffer auto begin() const -> iterator { return buf_.begin(); } auto end() const -> iterator { return buf_.end(); } - void advance_to(iterator it) { - buf_.consume(detail::to_unsigned(it - begin())); + void advance_to(iterator) { + // The scan_buffer iterator automatically updates the buffer position when + // incremented. } }; @@ -242,17 +324,18 @@ struct scan_handler : error_handler { } public: - FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf, scan_args args) + FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf, + scan_args args) : parse_ctx_(format), scan_ctx_(buf), args_(args), next_arg_id_(0) {} - auto pos() const -> const char* { return scan_ctx_.begin(); } + auto pos() const -> scan_buffer::iterator { return scan_ctx_.begin(); } void on_text(const char* begin, const char* end) { - auto size = to_unsigned(end - begin); - auto it = scan_ctx_.begin(); - if (it + size > scan_ctx_.end() || !std::equal(begin, end, it)) - on_error("invalid input"); - scan_ctx_.advance_to(it + size); + auto it = scan_ctx_.begin(), scan_end = scan_ctx_.end(); + for (; begin != end; ++begin, ++it) { + if (it == scan_end || *begin != *it) on_error("invalid input"); + } + scan_ctx_.advance_to(it); } FMT_CONSTEXPR auto on_arg_id() -> int { return on_arg_id(next_arg_id_++); } @@ -286,9 +369,14 @@ struct scan_handler : error_handler { scan_ctx_.advance_to(it); break; case scan_type::string_view_type: { - auto s = it; - while (it != end && *it != ' ') ++it; - *arg_.string_view = fmt::string_view(s, to_unsigned(it - s)); + auto range = to_contiguous(it); + // This could also be checked at compile time in scan. + if (!range) on_error("string_view requires contiguous input"); + auto p = range.begin; + while (p != range.end && *p != ' ') ++p; + size_t size = to_unsigned(p - range.begin); + *arg_.string_view = {range.begin, size}; + advance(it, size); scan_ctx_.advance_to(it); break; } @@ -322,11 +410,10 @@ auto scan(string_view input, string_view fmt, T&... args) -> string_view::iterator { auto&& buf = detail::string_scan_buffer(input); vscan(buf, fmt, make_scan_args(args...)); - return input.begin() + (buf.begin() - input.data()); + return input.begin() + (buf.begin().base() - input.data()); } -template -void scan(std::FILE* f, string_view fmt, T&... args) { +template void scan(std::FILE* f, string_view fmt, T&... args) { auto&& buf = detail::file_scan_buffer(f); vscan(buf, fmt, make_scan_args(args...)); } -- cgit v1.2.3 From 573d74395b3b0e745ea8c8de6bb7cde2bbade96a Mon Sep 17 00:00:00 2001 From: js324 Date: Tue, 5 Dec 2023 16:45:10 -0500 Subject: error on bool arg w/ char pres_type (#3734) --- include/fmt/core.h | 1 + test/format-test.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/fmt/core.h b/include/fmt/core.h index 2fcaf845..b19abf37 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2429,6 +2429,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( case 'G': return parse_presentation_type(pres::general_upper, float_set); case 'c': + if (arg_type == type::bool_type) throw_format_error("invalid format specifier"); return parse_presentation_type(pres::chr, integral_set); case 's': return parse_presentation_type(pres::string, diff --git a/test/format-test.cc b/test/format-test.cc index 34eb28a3..0a9924bf 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1159,6 +1159,8 @@ TEST(format_test, format_bool) { EXPECT_EQ("true", fmt::format("{:s}", true)); EXPECT_EQ("false", fmt::format("{:s}", false)); EXPECT_EQ("false ", fmt::format("{:6s}", false)); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:c}"), false), format_error, + "invalid format specifier"); } TEST(format_test, format_short) { -- cgit v1.2.3 From 6f95000b7ad8c2fa63af1cf80da4da1992f87917 Mon Sep 17 00:00:00 2001 From: reinhardt1053 Date: Thu, 7 Dec 2023 15:21:06 +0100 Subject: Update README.rst (#3737) Fix Celestia URL --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 81948bd6..8f8657a4 100644 --- a/README.rst +++ b/README.rst @@ -323,7 +323,7 @@ Projects using this library * `Blizzard Battle.net `_: an online gaming platform -* `Celestia `_: real-time 3D visualization of space +* `Celestia `_: real-time 3D visualization of space * `Ceph `_: a scalable distributed storage system -- cgit v1.2.3 From 81629e425c6baf90dff71eda574fd883817fb9bd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 8 Dec 2023 07:28:33 -0800 Subject: Convert README to Markdown --- README.md | 494 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.rst | 553 ------------------------------------------------------------- 2 files changed, 494 insertions(+), 553 deletions(-) create mode 100644 README.md delete mode 100644 README.rst diff --git a/README.md b/README.md new file mode 100644 index 00000000..8b590fcf --- /dev/null +++ b/README.md @@ -0,0 +1,494 @@ +{fmt} + +[![image](https://github.com/fmtlib/fmt/workflows/linux/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux) +[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos) +[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows) +[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1) +[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt) +[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt) + +**{fmt}** is an open-source formatting library providing a fast and safe +alternative to C stdio and C++ iostreams. + +If you like this project, please consider donating to one of the funds +that help victims of the war in Ukraine: . + +[Documentation](https://fmt.dev) + +[Cheat Sheets](https://hackingcpp.com/cpp/libs/fmt.html) + +Q&A: ask questions on [StackOverflow with the tag +fmt](https://stackoverflow.com/questions/tagged/fmt). + +Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). + +# Features + +- Simple [format API](https://fmt.dev/latest/api.html) with positional + arguments for localization +- Implementation of [C++20 + std::format](https://en.cppreference.com/w/cpp/utility/format) and + [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) +- [Format string syntax](https://fmt.dev/latest/syntax.html) similar + to Python\'s + [format](https://docs.python.org/3/library/stdtypes.html#str.format) +- Fast IEEE 754 floating-point formatter with correct rounding, + shortness and round-trip guarantees using the + [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm +- Portable Unicode support +- Safe [printf + implementation](https://fmt.dev/latest/api.html#printf-formatting) + including the POSIX extension for positional arguments +- Extensibility: [support for user-defined + types](https://fmt.dev/latest/api.html#formatting-user-defined-types) +- High performance: faster than common standard library + implementations of `(s)printf`, iostreams, `to_string` and + `to_chars`, see [Speed tests](#speed-tests) and [Converting a + hundred million integers to strings per + second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html) +- Small code size both in terms of source code with the minimum + configuration consisting of just three files, `core.h`, `format.h` + and `format-inl.h`, and compiled code; see [Compile time and code + bloat](#compile-time-and-code-bloat) +- Reliability: the library has an extensive set of + [tests](https://github.com/fmtlib/fmt/tree/master/test) and is + [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1) +- Safety: the library is fully type-safe, errors in format strings can + be reported at compile time, automatic memory management prevents + buffer overflow errors +- Ease of use: small self-contained code base, no external + dependencies, permissive MIT + [license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) +- [Portability](https://fmt.dev/latest/index.html#portability) with + consistent output across platforms and support for older compilers +- Clean warning-free codebase even on high warning levels such as + `-Wall -Wextra -pedantic` +- Locale independence by default +- Optional header-only configuration enabled with the + `FMT_HEADER_ONLY` macro + +See the [documentation](https://fmt.dev) for more details. + +# Examples + +**Print to stdout** ([run](https://godbolt.org/z/Tevcjh)) + +``` c++ +#include + +int main() { + fmt::print("Hello, world!\n"); +} +``` + +**Format a string** ([run](https://godbolt.org/z/oK8h33)) + +``` c++ +std::string s = fmt::format("The answer is {}.", 42); +// s == "The answer is 42." +``` + +**Format a string using positional arguments** +([run](https://godbolt.org/z/Yn7Txe)) + +``` c++ +std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); +// s == "I'd rather be happy than right." +``` + +**Print chrono durations** ([run](https://godbolt.org/z/K8s4Mc)) + +``` c++ +#include + +int main() { + using namespace std::literals::chrono_literals; + fmt::print("Default format: {} {}\n", 42s, 100ms); + fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); +} +``` + +Output: + + Default format: 42s 100ms + strftime-like format: 03:15:30 + +**Print a container** ([run](https://godbolt.org/z/MxM1YqjE7)) + +``` c++ +#include +#include + +int main() { + std::vector v = {1, 2, 3}; + fmt::print("{}\n", v); +} +``` + +Output: + + [1, 2, 3] + +**Check a format string at compile time** + +``` c++ +std::string s = fmt::format("{:d}", "I am not a number"); +``` + +This gives a compile-time error in C++20 because `d` is an invalid +format specifier for a string. + +**Write a file from a single thread** + +``` c++ +#include + +int main() { + auto out = fmt::output_file("guide.txt"); + out.print("Don't {}", "Panic"); +} +``` + +This can be [5 to 9 times faster than +fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html). + +**Print with colors and text styles** + +``` c++ +#include + +int main() { + fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, + "Hello, {}!\n", "world"); + fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | + fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); + fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, + "你好{}!\n", "世界"); +} +``` + +Output on a modern terminal with Unicode support: + +![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7) + +# Benchmarks + +## Speed tests + ++-------------------+---------------+-------------+ +| Library | Method | Run Time, s | ++===================+===============+=============+ +| libc | printf | > 0.91 | ++-------------------+---------------+-------------+ +| libc++ | std::ostream | > 2.49 | ++-------------------+---------------+-------------+ +| {fmt} 9.1 | fmt::print | > 0.74 | ++-------------------+---------------+-------------+ +| Boost Format 1.80 | boost::format | > 6.26 | ++-------------------+---------------+-------------+ +| Folly Format | folly::format | > 1.87 | ++-------------------+---------------+-------------+ + +{fmt} is the fastest of the benchmarked methods, \~20% faster than +`printf`. + +The above results were generated by building `tinyformat_test.cpp` on +macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and +taking the best of three runs. In the test, the format string +`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000 +times with output sent to `/dev/null`; for further details refer to the +[source](https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc). + +{fmt} is up to 20-30x faster than `std::ostringstream` and `sprintf` on +IEEE754 `float` and `double` formatting +([dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark)) and faster +than [double-conversion](https://github.com/google/double-conversion) +and [ryu](https://github.com/ulfjack/ryu): + +[![image](https://user-images.githubusercontent.com/576385/%0A95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html) + +## Compile time and code bloat + +The script +[bloat-test.py](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py) +from [format-benchmark](https://github.com/fmtlib/format-benchmark) +tests compile time and code bloat for nontrivial projects. It generates +100 translation units and uses `printf()` or its alternative five times +in each to simulate a medium-sized project. The resulting executable +size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), macOS +Sierra, best of three) is shown in the following tables. + +**Optimized build (-O3)** + +| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | +| ------------- | --------------- | -------------------- | ------------------ | +| printf | > 2.6 | > 29 | > 26 | +| printf+string | > 16.4 | > 29 | > 26 | +| iostreams | > 31.1 | > 59 | > 55 | +| {fmt} | > 19.0 | > 37 | > 34 | +| Boost Format | > 91.9 | > 226 | > 203 | +| Folly Format | > 115.7 | > 101 | > 88 | + +As you can see, {fmt} has 60% less overhead in terms of resulting binary +code size compared to iostreams and comes pretty close to `printf`. +Boost Format and Folly Format have the largest overheads. + +`printf+string` is the same as `printf` but with an extra `` +include to measure the overhead of the latter. + +**Non-optimized build** + +| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | +| ------------- | --------------- | -------------------- | ------------------ | +| printf | > 2.2 | > 33 | > 30 | +| printf+string | > 16.0 | > 33 | > 30 | +| iostreams | > 28.3 | > 56 | > 52 | +| {fmt} | > 18.2 | > 59 | > 50 | +| Boost Format | > 54.1 | > 365 | > 303 | +| Folly Format | > 79.9 | > 445 | > 430 | + +`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries +to compare formatting function overhead only. Boost Format is a +header-only library so it doesn\'t provide any linkage options. + +## Running the tests + +Please refer to [Building the +library](https://fmt.dev/latest/usage.html#building-the-library) for +instructions on how to build the library and run the unit tests. + +Benchmarks reside in a separate repository, +[format-benchmarks](https://github.com/fmtlib/format-benchmark), so to +run the benchmarks you first need to clone this repository and generate +Makefiles with CMake: + + $ git clone --recursive https://github.com/fmtlib/format-benchmark.git + $ cd format-benchmark + $ cmake . + +Then you can run the speed test: + + $ make speed-test + +or the bloat test: + + $ make bloat-test + +# Migrating code + +[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v17 (not yet +released) provides the +[modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html) +check that is capable of converting occurrences of `printf` and +`fprintf` to `fmt::print` if configured to do so. (By default it +converts to `std::print`.) + +# Projects using this library + +- [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform + real-time strategy game +- [AMPL/MP](https://github.com/ampl/mp): an open-source library for + mathematical programming +- [Aseprite](https://github.com/aseprite/aseprite): animated sprite + editor & pixel art tool +- [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft + operations suite +- [Blizzard Battle.net](https://battle.net/): an online gaming + platform +- [Celestia](https://celestia.space/): real-time 3D visualization of + space +- [Ceph](https://ceph.com/): a scalable distributed storage system +- [ccache](https://ccache.dev/): a compiler cache +- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an + analytical database management system +- [Contour](https://github.com/contour-terminal/contour/): a modern + terminal emulator +- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous + underwater vehicle +- [Drake](https://drake.mit.edu/): a planning, control, and analysis + toolbox for nonlinear dynamical systems (MIT) +- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and + communication bus (Lyft) +- [FiveM](https://fivem.net/): a modification framework for GTA V +- [fmtlog](https://github.com/MengRao/fmtlog): a performant + fmtlib-style logging library with latency in nanoseconds +- [Folly](https://github.com/facebook/folly): Facebook open-source + library +- [GemRB](https://gemrb.org/): a portable open-source implementation + of Bioware's Infinity Engine +- [Grand Mountain + Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/): + a beautiful open-world ski & snowboarding game +- [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs + Player Gaming Network with tweaks +- [KBEngine](https://github.com/kbengine/kbengine): an open-source + MMOG server engine +- [Keypirinha](https://keypirinha.com/): a semantic launcher for + Windows +- [Kodi](https://kodi.tv/) (formerly xbmc): home theater software +- [Knuth](https://kth.cash/): high-performance Bitcoin full-node +- [libunicode](https://github.com/contour-terminal/libunicode/): a + modern C++17 Unicode library +- [MariaDB](https://mariadb.org/): relational database management + system +- [Microsoft Verona](https://github.com/microsoft/verona): research + programming language for concurrent ownership +- [MongoDB](https://mongodb.com/): distributed document database +- [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small + tool to generate randomized datasets +- [OpenSpace](https://openspaceproject.com/): an open-source + astrovisualization framework +- [PenUltima Online (POL)](https://www.polserver.com/): an MMO server, + compatible with most Ultima Online clients +- [PyTorch](https://github.com/pytorch/pytorch): an open-source + machine learning library +- [quasardb](https://www.quasardb.net/): a distributed, + high-performance, associative database +- [Quill](https://github.com/odygrd/quill): asynchronous low-latency + logging library +- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to + simplify navigation, and executing complex multi-line terminal + command sequences +- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis + cluster proxy +- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka® + replacement for mission-critical systems written in C++ +- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and + client library +- [Salesforce Analytics + Cloud](https://www.salesforce.com/analytics-cloud/overview/): + business intelligence software +- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL + data store that can handle 1 million transactions per second on a + single server +- [Seastar](http://www.seastar-project.org/): an advanced, open-source + C++ framework for high-performance server applications on modern + hardware +- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging + library +- [Stellar](https://www.stellar.org/): financial platform +- [Touch Surgery](https://www.touchsurgery.com/): surgery simulator +- [TrinityCore](https://github.com/TrinityCore/TrinityCore): + open-source MMORPG framework +- [🐙 userver framework](https://userver.tech/): open-source + asynchronous framework with a rich set of abstractions and database + drivers +- [Windows Terminal](https://github.com/microsoft/terminal): the new + Windows terminal + +[More\...](https://github.com/search?q=fmtlib&type=Code) + +If you are aware of other projects using this library, please let me +know by [email](mailto:victor.zverovich@gmail.com) or by submitting an +[issue](https://github.com/fmtlib/fmt/issues). + +# Motivation + +So why yet another formatting library? + +There are plenty of methods for doing this task, from standard ones like +the printf family of function and iostreams to Boost Format and +FastFormat libraries. The reason for creating a new library is that +every existing solution that I found either had serious issues or +didn\'t provide all the features I needed. + +## printf + +The good thing about `printf` is that it is pretty fast and readily +available being a part of the C standard library. The main drawback is +that it doesn\'t support user-defined types. `printf` also has safety +issues although they are somewhat mitigated with [\_\_attribute\_\_ +((format (printf, +\...))](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html) in +GCC. There is a POSIX extension that adds positional arguments required +for +[i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization) +to `printf` but it is not a part of C99 and may not be available on some +platforms. + +## iostreams + +The main issue with iostreams is best illustrated with an example: + +``` c++ +std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; +``` + +which is a lot of typing compared to printf: + +``` c++ +printf("%.2f\n", 1.23456); +``` + +Matthew Wilson, the author of FastFormat, called this \"chevron hell\". +iostreams don\'t support positional arguments by design. + +The good part is that iostreams support user-defined types and are safe +although error handling is awkward. + +## Boost Format + +This is a very powerful library that supports both `printf`-like format +strings and positional arguments. Its main drawback is performance. +According to various benchmarks, it is much slower than other methods +considered here. Boost Format also has excessive build times and severe +code bloat issues (see [Benchmarks](#benchmarks)). + +## FastFormat + +This is an interesting library that is fast, safe, and has positional +arguments. However, it has significant limitations, citing its author: + +> Three features that have no hope of being accommodated within the +> current design are: +> +> - Leading zeros (or any other non-space padding) +> - Octal/hexadecimal encoding +> - Runtime width/alignment specification + +It is also quite big and has a heavy dependency, STLSoft, which might be +too restrictive for using it in some projects. + +## Boost Spirit.Karma + +This is not a formatting library but I decided to include it here for +completeness. As iostreams, it suffers from the problem of mixing +verbatim text with arguments. The library is pretty fast, but slower on +integer formatting than `fmt::format_to` with format string compilation +on Karma\'s own benchmark, see [Converting a hundred million integers to +strings per +second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html). + +# License + +{fmt} is distributed under the MIT +[license](https://github.com/fmtlib/fmt/blob/master/LICENSE). + +# Documentation License + +The [Format String Syntax](https://fmt.dev/latest/syntax.html) section +in the documentation is based on the one from Python [string module +documentation](https://docs.python.org/3/library/string.html#module-string). +For this reason, the documentation is distributed under the Python +Software Foundation license available in +[doc/python-license.txt](https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt). +It only applies if you distribute the documentation of {fmt}. + +# Maintainers + +The {fmt} library is maintained by Victor Zverovich +([vitaut](https://github.com/vitaut)) with contributions from many other +people. See +[Contributors](https://github.com/fmtlib/fmt/graphs/contributors) and +[Releases](https://github.com/fmtlib/fmt/releases) for some of the +names. Let us know if your contribution is not listed or mentioned +incorrectly and we\'ll make it right. + +# Security Policy + +To report a security issue, please disclose it at [security +advisory](https://github.com/fmtlib/fmt/security/advisories/new). + +This project is maintained by a team of volunteers on a +reasonable-effort basis. As such, please give us at least 90 days to +work on a fix before public exposure. diff --git a/README.rst b/README.rst deleted file mode 100644 index 8f8657a4..00000000 --- a/README.rst +++ /dev/null @@ -1,553 +0,0 @@ -.. image:: https://user-images.githubusercontent.com/ - 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png - :width: 25% - :alt: {fmt} - -.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux - -.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos - -.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg - :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows - -.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg - :alt: fmt is continuously fuzzed at oss-fuzz - :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\ - colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\ - Summary&q=proj%3Dfmt&can=1 - -.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg - :alt: Ask questions at StackOverflow with the tag fmt - :target: https://stackoverflow.com/questions/tagged/fmt - -.. image:: https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge - :target: https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt - -**{fmt}** is an open-source formatting library providing a fast and safe -alternative to C stdio and C++ iostreams. - -If you like this project, please consider donating to one of the funds that -help victims of the war in Ukraine: https://www.stopputin.net/. - -`Documentation `__ - -`Cheat Sheets `__ - -Q&A: ask questions on `StackOverflow with the tag fmt -`_. - -Try {fmt} in `Compiler Explorer `_. - -Features --------- - -* Simple `format API `_ with positional arguments - for localization -* Implementation of `C++20 std::format - `__ and `C++23 std::print - `__ -* `Format string syntax `_ similar to Python's - `format `_ -* Fast IEEE 754 floating-point formatter with correct rounding, shortness and - round-trip guarantees using the `Dragonbox `_ - algorithm -* Portable Unicode support -* Safe `printf implementation - `_ including the POSIX - extension for positional arguments -* Extensibility: `support for user-defined types - `_ -* High performance: faster than common standard library implementations of - ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ - and `Converting a hundred million integers to strings per second - `_ -* Small code size both in terms of source code with the minimum configuration - consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``, - and compiled code; see `Compile time and code bloat`_ -* Reliability: the library has an extensive set of `tests - `_ and is `continuously fuzzed - `_ -* Safety: the library is fully type-safe, errors in format strings can be - reported at compile time, automatic memory management prevents buffer overflow - errors -* Ease of use: small self-contained code base, no external dependencies, - permissive MIT `license - `_ -* `Portability `_ with - consistent output across platforms and support for older compilers -* Clean warning-free codebase even on high warning levels such as - ``-Wall -Wextra -pedantic`` -* Locale independence by default -* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro - -See the `documentation `_ for more details. - -Examples --------- - -**Print to stdout** (`run `_) - -.. code:: c++ - - #include - - int main() { - fmt::print("Hello, world!\n"); - } - -**Format a string** (`run `_) - -.. code:: c++ - - std::string s = fmt::format("The answer is {}.", 42); - // s == "The answer is 42." - -**Format a string using positional arguments** (`run `_) - -.. code:: c++ - - std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); - // s == "I'd rather be happy than right." - -**Print chrono durations** (`run `_) - -.. code:: c++ - - #include - - int main() { - using namespace std::literals::chrono_literals; - fmt::print("Default format: {} {}\n", 42s, 100ms); - fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); - } - -Output:: - - Default format: 42s 100ms - strftime-like format: 03:15:30 - -**Print a container** (`run `_) - -.. code:: c++ - - #include - #include - - int main() { - std::vector v = {1, 2, 3}; - fmt::print("{}\n", v); - } - -Output:: - - [1, 2, 3] - -**Check a format string at compile time** - -.. code:: c++ - - std::string s = fmt::format("{:d}", "I am not a number"); - -This gives a compile-time error in C++20 because ``d`` is an invalid format -specifier for a string. - -**Write a file from a single thread** - -.. code:: c++ - - #include - - int main() { - auto out = fmt::output_file("guide.txt"); - out.print("Don't {}", "Panic"); - } - -This can be `5 to 9 times faster than fprintf -`_. - -**Print with colors and text styles** - -.. code:: c++ - - #include - - int main() { - fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, - "Hello, {}!\n", "world"); - fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) | - fmt::emphasis::underline, "Olá, {}!\n", "Mundo"); - fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic, - "你好{}!\n", "世界"); - } - -Output on a modern terminal with Unicode support: - -.. image:: https://github.com/fmtlib/fmt/assets/ - 576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7 - -Benchmarks ----------- - -Speed tests -~~~~~~~~~~~ - -================= ============= =========== -Library Method Run Time, s -================= ============= =========== -libc printf 0.91 -libc++ std::ostream 2.49 -{fmt} 9.1 fmt::print 0.74 -Boost Format 1.80 boost::format 6.26 -Folly Format folly::format 1.87 -================= ============= =========== - -{fmt} is the fastest of the benchmarked methods, ~20% faster than ``printf``. - -The above results were generated by building ``tinyformat_test.cpp`` on macOS -12.6.1 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the -best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` -or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for -further details refer to the `source -`_. - -{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on -IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark `_) -and faster than `double-conversion `_ and -`ryu `_: - -.. image:: https://user-images.githubusercontent.com/576385/ - 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png - :target: https://fmt.dev/unknown_mac64_clang12.0.html - -Compile time and code bloat -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The script `bloat-test.py -`_ -from `format-benchmark `_ -tests compile time and code bloat for nontrivial projects. -It generates 100 translation units and uses ``printf()`` or its alternative -five times in each to simulate a medium-sized project. The resulting -executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), -macOS Sierra, best of three) is shown in the following tables. - -**Optimized build (-O3)** - -============= =============== ==================== ================== -Method Compile Time, s Executable size, KiB Stripped size, KiB -============= =============== ==================== ================== -printf 2.6 29 26 -printf+string 16.4 29 26 -iostreams 31.1 59 55 -{fmt} 19.0 37 34 -Boost Format 91.9 226 203 -Folly Format 115.7 101 88 -============= =============== ==================== ================== - -As you can see, {fmt} has 60% less overhead in terms of resulting binary code -size compared to iostreams and comes pretty close to ``printf``. Boost Format -and Folly Format have the largest overheads. - -``printf+string`` is the same as ``printf`` but with an extra ```` -include to measure the overhead of the latter. - -**Non-optimized build** - -============= =============== ==================== ================== -Method Compile Time, s Executable size, KiB Stripped size, KiB -============= =============== ==================== ================== -printf 2.2 33 30 -printf+string 16.0 33 30 -iostreams 28.3 56 52 -{fmt} 18.2 59 50 -Boost Format 54.1 365 303 -Folly Format 79.9 445 430 -============= =============== ==================== ================== - -``libc``, ``lib(std)c++``, and ``libfmt`` are all linked as shared libraries to -compare formatting function overhead only. Boost Format is a -header-only library so it doesn't provide any linkage options. - -Running the tests -~~~~~~~~~~~~~~~~~ - -Please refer to `Building the library`__ for instructions on how to build -the library and run the unit tests. - -__ https://fmt.dev/latest/usage.html#building-the-library - -Benchmarks reside in a separate repository, -`format-benchmarks `_, -so to run the benchmarks you first need to clone this repository and -generate Makefiles with CMake:: - - $ git clone --recursive https://github.com/fmtlib/format-benchmark.git - $ cd format-benchmark - $ cmake . - -Then you can run the speed test:: - - $ make speed-test - -or the bloat test:: - - $ make bloat-test - -Migrating code --------------- - -`clang-tidy `_ v17 (not yet -released) provides the `modernize-use-std-print -`_ -check that is capable of converting occurrences of ``printf`` and -``fprintf`` to ``fmt::print`` if configured to do so. (By default it -converts to ``std::print``.) - -Projects using this library ---------------------------- - -* `0 A.D. `_: a free, open-source, cross-platform - real-time strategy game - -* `AMPL/MP `_: - an open-source library for mathematical programming - -* `Aseprite `_: - animated sprite editor & pixel art tool - -* `AvioBook `_: a comprehensive aircraft - operations suite - -* `Blizzard Battle.net `_: an online gaming platform - -* `Celestia `_: real-time 3D visualization of space - -* `Ceph `_: a scalable distributed storage system - -* `ccache `_: a compiler cache - -* `ClickHouse `_: an analytical database - management system - -* `Contour `_: a modern terminal emulator - -* `CUAUV `_: Cornell University's autonomous underwater - vehicle - -* `Drake `_: a planning, control, and analysis toolbox - for nonlinear dynamical systems (MIT) - -* `Envoy `_: C++ L7 proxy and communication bus - (Lyft) - -* `FiveM `_: a modification framework for GTA V - -* `fmtlog `_: a performant fmtlib-style - logging library with latency in nanoseconds - -* `Folly `_: Facebook open-source library - -* `GemRB `_: a portable open-source implementation of - Bioware’s Infinity Engine - -* `Grand Mountain Adventure - `_: - a beautiful open-world ski & snowboarding game - -* `HarpyWar/pvpgn `_: - Player vs Player Gaming Network with tweaks - -* `KBEngine `_: an open-source MMOG server - engine - -* `Keypirinha `_: a semantic launcher for Windows - -* `Kodi `_ (formerly xbmc): home theater software - -* `Knuth `_: high-performance Bitcoin full-node - -* `libunicode `_: a modern C++17 Unicode library - -* `MariaDB `_: relational database management system - -* `Microsoft Verona `_: - research programming language for concurrent ownership - -* `MongoDB `_: distributed document database - -* `MongoDB Smasher `_: a small tool to - generate randomized datasets - -* `OpenSpace `_: an open-source - astrovisualization framework - -* `PenUltima Online (POL) `_: - an MMO server, compatible with most Ultima Online clients - -* `PyTorch `_: an open-source machine - learning library - -* `quasardb `_: a distributed, high-performance, - associative database - -* `Quill `_: asynchronous low-latency logging library - -* `QKW `_: generalizing aliasing to simplify - navigation, and executing complex multi-line terminal command sequences - -* `redis-cerberus `_: a Redis cluster - proxy - -* `redpanda `_: a 10x faster Kafka® replacement - for mission-critical systems written in C++ - -* `rpclib `_: a modern C++ msgpack-RPC server and client - library - -* `Salesforce Analytics Cloud - `_: - business intelligence software - -* `Scylla `_: a Cassandra-compatible NoSQL data store - that can handle 1 million transactions per second on a single server - -* `Seastar `_: an advanced, open-source C++ - framework for high-performance server applications on modern hardware - -* `spdlog `_: super fast C++ logging library - -* `Stellar `_: financial platform - -* `Touch Surgery `_: surgery simulator - -* `TrinityCore `_: open-source - MMORPG framework - -* `🐙 userver framework `_: open-source asynchronous - framework with a rich set of abstractions and database drivers - -* `Windows Terminal `_: the new Windows - terminal - -`More... `_ - -If you are aware of other projects using this library, please let me know -by `email `_ or by submitting an -`issue `_. - -Motivation ----------- - -So why yet another formatting library? - -There are plenty of methods for doing this task, from standard ones like -the printf family of function and iostreams to Boost Format and FastFormat -libraries. The reason for creating a new library is that every existing -solution that I found either had serious issues or didn't provide -all the features I needed. - -printf -~~~~~~ - -The good thing about ``printf`` is that it is pretty fast and readily available -being a part of the C standard library. The main drawback is that it -doesn't support user-defined types. ``printf`` also has safety issues although -they are somewhat mitigated with `__attribute__ ((format (printf, ...)) -`_ in GCC. -There is a POSIX extension that adds positional arguments required for -`i18n `_ -to ``printf`` but it is not a part of C99 and may not be available on some -platforms. - -iostreams -~~~~~~~~~ - -The main issue with iostreams is best illustrated with an example: - -.. code:: c++ - - std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n"; - -which is a lot of typing compared to printf: - -.. code:: c++ - - printf("%.2f\n", 1.23456); - -Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams -don't support positional arguments by design. - -The good part is that iostreams support user-defined types and are safe although -error handling is awkward. - -Boost Format -~~~~~~~~~~~~ - -This is a very powerful library that supports both ``printf``-like format -strings and positional arguments. Its main drawback is performance. According to -various benchmarks, it is much slower than other methods considered here. Boost -Format also has excessive build times and severe code bloat issues (see -`Benchmarks`_). - -FastFormat -~~~~~~~~~~ - -This is an interesting library that is fast, safe, and has positional arguments. -However, it has significant limitations, citing its author: - - Three features that have no hope of being accommodated within the - current design are: - - * Leading zeros (or any other non-space padding) - * Octal/hexadecimal encoding - * Runtime width/alignment specification - -It is also quite big and has a heavy dependency, STLSoft, which might be too -restrictive for using it in some projects. - -Boost Spirit.Karma -~~~~~~~~~~~~~~~~~~ - -This is not a formatting library but I decided to include it here for -completeness. As iostreams, it suffers from the problem of mixing verbatim text -with arguments. The library is pretty fast, but slower on integer formatting -than ``fmt::format_to`` with format string compilation on Karma's own benchmark, -see `Converting a hundred million integers to strings per second -`_. - -License -------- - -{fmt} is distributed under the MIT `license -`_. - -Documentation License ---------------------- - -The `Format String Syntax `_ -section in the documentation is based on the one from Python `string module -documentation `_. -For this reason, the documentation is distributed under the Python Software -Foundation license available in `doc/python-license.txt -`_. -It only applies if you distribute the documentation of {fmt}. - -Maintainers ------------ - -The {fmt} library is maintained by Victor Zverovich (`vitaut -`_) with contributions from many other people. -See `Contributors `_ and -`Releases `_ for some of the names. -Let us know if your contribution is not listed or mentioned incorrectly and -we'll make it right. - -Security Policy ---------------- - -To report a security issue, please disclose it at `security advisory `_. - -This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. -- cgit v1.2.3 From 4497a2d09adea0ea78cfbb365f433bb32044c70c Mon Sep 17 00:00:00 2001 From: H1X4 <10332146+H1X4Dev@users.noreply.github.com> Date: Fri, 8 Dec 2023 18:13:18 +0200 Subject: fix cmake build --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 639c44f4..4b928ae3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,7 +283,7 @@ if (FMT_OS) endif () add_module_library(fmt src/fmt.cc FALLBACK - ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.md + ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md IF FMT_MODULE) add_library(fmt::fmt ALIAS fmt) if (FMT_MODULE) @@ -448,6 +448,6 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore}) set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) set(CPACK_PACKAGE_NAME fmt) - set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst) + set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md) include(CPack) endif () -- cgit v1.2.3 From 9c3c107c8c233a4d31a88745c383414bf821a6b3 Mon Sep 17 00:00:00 2001 From: Albert Santoni Date: Fri, 8 Dec 2023 12:36:33 -0500 Subject: Fix compile with GCC 6.3.0 (bug #3738) (#3743) --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index b19abf37..baedccc1 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -817,7 +817,7 @@ template class buffer { protected: // Don't initialize ptr_ since it is not accessed to save a few cycles. FMT_MSC_WARNING(suppress : 26495) - buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} + FMT_CONSTEXPR buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {} FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept : ptr_(p), size_(sz), capacity_(cap) {} -- cgit v1.2.3 From 2fabb43b93ed6b9efbae9a429bf20ec56e13dc30 Mon Sep 17 00:00:00 2001 From: George Liontos Date: Fri, 8 Dec 2023 20:26:52 +0200 Subject: Fix README file table (#3744) Co-authored-by: George Liontos --- README.md | 282 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 138 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 8b590fcf..2e591937 100644 --- a/README.md +++ b/README.md @@ -24,48 +24,48 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). # Features -- Simple [format API](https://fmt.dev/latest/api.html) with positional - arguments for localization -- Implementation of [C++20 - std::format](https://en.cppreference.com/w/cpp/utility/format) and - [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) -- [Format string syntax](https://fmt.dev/latest/syntax.html) similar - to Python\'s - [format](https://docs.python.org/3/library/stdtypes.html#str.format) -- Fast IEEE 754 floating-point formatter with correct rounding, - shortness and round-trip guarantees using the - [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm -- Portable Unicode support -- Safe [printf - implementation](https://fmt.dev/latest/api.html#printf-formatting) - including the POSIX extension for positional arguments -- Extensibility: [support for user-defined - types](https://fmt.dev/latest/api.html#formatting-user-defined-types) -- High performance: faster than common standard library - implementations of `(s)printf`, iostreams, `to_string` and - `to_chars`, see [Speed tests](#speed-tests) and [Converting a - hundred million integers to strings per - second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html) -- Small code size both in terms of source code with the minimum - configuration consisting of just three files, `core.h`, `format.h` - and `format-inl.h`, and compiled code; see [Compile time and code - bloat](#compile-time-and-code-bloat) -- Reliability: the library has an extensive set of - [tests](https://github.com/fmtlib/fmt/tree/master/test) and is - [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1) -- Safety: the library is fully type-safe, errors in format strings can - be reported at compile time, automatic memory management prevents - buffer overflow errors -- Ease of use: small self-contained code base, no external - dependencies, permissive MIT - [license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) -- [Portability](https://fmt.dev/latest/index.html#portability) with - consistent output across platforms and support for older compilers -- Clean warning-free codebase even on high warning levels such as - `-Wall -Wextra -pedantic` -- Locale independence by default -- Optional header-only configuration enabled with the - `FMT_HEADER_ONLY` macro +- Simple [format API](https://fmt.dev/latest/api.html) with positional + arguments for localization +- Implementation of [C++20 + std::format](https://en.cppreference.com/w/cpp/utility/format) and + [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) +- [Format string syntax](https://fmt.dev/latest/syntax.html) similar + to Python\'s + [format](https://docs.python.org/3/library/stdtypes.html#str.format) +- Fast IEEE 754 floating-point formatter with correct rounding, + shortness and round-trip guarantees using the + [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm +- Portable Unicode support +- Safe [printf + implementation](https://fmt.dev/latest/api.html#printf-formatting) + including the POSIX extension for positional arguments +- Extensibility: [support for user-defined + types](https://fmt.dev/latest/api.html#formatting-user-defined-types) +- High performance: faster than common standard library + implementations of `(s)printf`, iostreams, `to_string` and + `to_chars`, see [Speed tests](#speed-tests) and [Converting a + hundred million integers to strings per + second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html) +- Small code size both in terms of source code with the minimum + configuration consisting of just three files, `core.h`, `format.h` + and `format-inl.h`, and compiled code; see [Compile time and code + bloat](#compile-time-and-code-bloat) +- Reliability: the library has an extensive set of + [tests](https://github.com/fmtlib/fmt/tree/master/test) and is + [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1) +- Safety: the library is fully type-safe, errors in format strings can + be reported at compile time, automatic memory management prevents + buffer overflow errors +- Ease of use: small self-contained code base, no external + dependencies, permissive MIT + [license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) +- [Portability](https://fmt.dev/latest/index.html#portability) with + consistent output across platforms and support for older compilers +- Clean warning-free codebase even on high warning levels such as + `-Wall -Wextra -pedantic` +- Locale independence by default +- Optional header-only configuration enabled with the + `FMT_HEADER_ONLY` macro See the [documentation](https://fmt.dev) for more details. @@ -175,19 +175,13 @@ Output on a modern terminal with Unicode support: ## Speed tests -+-------------------+---------------+-------------+ | Library | Method | Run Time, s | -+===================+===============+=============+ +|-------------------|---------------|-------------| | libc | printf | > 0.91 | -+-------------------+---------------+-------------+ | libc++ | std::ostream | > 2.49 | -+-------------------+---------------+-------------+ | {fmt} 9.1 | fmt::print | > 0.74 | -+-------------------+---------------+-------------+ | Boost Format 1.80 | boost::format | > 6.26 | -+-------------------+---------------+-------------+ | Folly Format | folly::format | > 1.87 | -+-------------------+---------------+-------------+ {fmt} is the fastest of the benchmarked methods, \~20% faster than `printf`. @@ -221,7 +215,7 @@ Sierra, best of three) is shown in the following tables. **Optimized build (-O3)** | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | -| ------------- | --------------- | -------------------- | ------------------ | +|---------------|-----------------|----------------------|--------------------| | printf | > 2.6 | > 29 | > 26 | | printf+string | > 16.4 | > 29 | > 26 | | iostreams | > 31.1 | > 59 | > 55 | @@ -239,7 +233,7 @@ include to measure the overhead of the latter. **Non-optimized build** | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | -| ------------- | --------------- | -------------------- | ------------------ | +|---------------|-----------------|----------------------|--------------------| | printf | > 2.2 | > 33 | > 30 | | printf+string | > 16.0 | > 33 | > 30 | | iostreams | > 28.3 | > 56 | > 52 | @@ -285,96 +279,96 @@ converts to `std::print`.) # Projects using this library -- [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform - real-time strategy game -- [AMPL/MP](https://github.com/ampl/mp): an open-source library for - mathematical programming -- [Aseprite](https://github.com/aseprite/aseprite): animated sprite - editor & pixel art tool -- [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft - operations suite -- [Blizzard Battle.net](https://battle.net/): an online gaming - platform -- [Celestia](https://celestia.space/): real-time 3D visualization of - space -- [Ceph](https://ceph.com/): a scalable distributed storage system -- [ccache](https://ccache.dev/): a compiler cache -- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an - analytical database management system -- [Contour](https://github.com/contour-terminal/contour/): a modern - terminal emulator -- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous - underwater vehicle -- [Drake](https://drake.mit.edu/): a planning, control, and analysis - toolbox for nonlinear dynamical systems (MIT) -- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and - communication bus (Lyft) -- [FiveM](https://fivem.net/): a modification framework for GTA V -- [fmtlog](https://github.com/MengRao/fmtlog): a performant - fmtlib-style logging library with latency in nanoseconds -- [Folly](https://github.com/facebook/folly): Facebook open-source - library -- [GemRB](https://gemrb.org/): a portable open-source implementation - of Bioware's Infinity Engine -- [Grand Mountain - Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/): - a beautiful open-world ski & snowboarding game -- [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs - Player Gaming Network with tweaks -- [KBEngine](https://github.com/kbengine/kbengine): an open-source - MMOG server engine -- [Keypirinha](https://keypirinha.com/): a semantic launcher for - Windows -- [Kodi](https://kodi.tv/) (formerly xbmc): home theater software -- [Knuth](https://kth.cash/): high-performance Bitcoin full-node -- [libunicode](https://github.com/contour-terminal/libunicode/): a - modern C++17 Unicode library -- [MariaDB](https://mariadb.org/): relational database management - system -- [Microsoft Verona](https://github.com/microsoft/verona): research - programming language for concurrent ownership -- [MongoDB](https://mongodb.com/): distributed document database -- [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small - tool to generate randomized datasets -- [OpenSpace](https://openspaceproject.com/): an open-source - astrovisualization framework -- [PenUltima Online (POL)](https://www.polserver.com/): an MMO server, - compatible with most Ultima Online clients -- [PyTorch](https://github.com/pytorch/pytorch): an open-source - machine learning library -- [quasardb](https://www.quasardb.net/): a distributed, - high-performance, associative database -- [Quill](https://github.com/odygrd/quill): asynchronous low-latency - logging library -- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to - simplify navigation, and executing complex multi-line terminal - command sequences -- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis - cluster proxy -- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka® - replacement for mission-critical systems written in C++ -- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and - client library -- [Salesforce Analytics - Cloud](https://www.salesforce.com/analytics-cloud/overview/): - business intelligence software -- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL - data store that can handle 1 million transactions per second on a - single server -- [Seastar](http://www.seastar-project.org/): an advanced, open-source - C++ framework for high-performance server applications on modern - hardware -- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging - library -- [Stellar](https://www.stellar.org/): financial platform -- [Touch Surgery](https://www.touchsurgery.com/): surgery simulator -- [TrinityCore](https://github.com/TrinityCore/TrinityCore): - open-source MMORPG framework -- [🐙 userver framework](https://userver.tech/): open-source - asynchronous framework with a rich set of abstractions and database - drivers -- [Windows Terminal](https://github.com/microsoft/terminal): the new - Windows terminal +- [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform + real-time strategy game +- [AMPL/MP](https://github.com/ampl/mp): an open-source library for + mathematical programming +- [Aseprite](https://github.com/aseprite/aseprite): animated sprite + editor & pixel art tool +- [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft + operations suite +- [Blizzard Battle.net](https://battle.net/): an online gaming + platform +- [Celestia](https://celestia.space/): real-time 3D visualization of + space +- [Ceph](https://ceph.com/): a scalable distributed storage system +- [ccache](https://ccache.dev/): a compiler cache +- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an + analytical database management system +- [Contour](https://github.com/contour-terminal/contour/): a modern + terminal emulator +- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous + underwater vehicle +- [Drake](https://drake.mit.edu/): a planning, control, and analysis + toolbox for nonlinear dynamical systems (MIT) +- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and + communication bus (Lyft) +- [FiveM](https://fivem.net/): a modification framework for GTA V +- [fmtlog](https://github.com/MengRao/fmtlog): a performant + fmtlib-style logging library with latency in nanoseconds +- [Folly](https://github.com/facebook/folly): Facebook open-source + library +- [GemRB](https://gemrb.org/): a portable open-source implementation + of Bioware's Infinity Engine +- [Grand Mountain + Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/): + a beautiful open-world ski & snowboarding game +- [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs + Player Gaming Network with tweaks +- [KBEngine](https://github.com/kbengine/kbengine): an open-source + MMOG server engine +- [Keypirinha](https://keypirinha.com/): a semantic launcher for + Windows +- [Kodi](https://kodi.tv/) (formerly xbmc): home theater software +- [Knuth](https://kth.cash/): high-performance Bitcoin full-node +- [libunicode](https://github.com/contour-terminal/libunicode/): a + modern C++17 Unicode library +- [MariaDB](https://mariadb.org/): relational database management + system +- [Microsoft Verona](https://github.com/microsoft/verona): research + programming language for concurrent ownership +- [MongoDB](https://mongodb.com/): distributed document database +- [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small + tool to generate randomized datasets +- [OpenSpace](https://openspaceproject.com/): an open-source + astrovisualization framework +- [PenUltima Online (POL)](https://www.polserver.com/): an MMO server, + compatible with most Ultima Online clients +- [PyTorch](https://github.com/pytorch/pytorch): an open-source + machine learning library +- [quasardb](https://www.quasardb.net/): a distributed, + high-performance, associative database +- [Quill](https://github.com/odygrd/quill): asynchronous low-latency + logging library +- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to + simplify navigation, and executing complex multi-line terminal + command sequences +- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis + cluster proxy +- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka® + replacement for mission-critical systems written in C++ +- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and + client library +- [Salesforce Analytics + Cloud](https://www.salesforce.com/analytics-cloud/overview/): + business intelligence software +- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL + data store that can handle 1 million transactions per second on a + single server +- [Seastar](http://www.seastar-project.org/): an advanced, open-source + C++ framework for high-performance server applications on modern + hardware +- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging + library +- [Stellar](https://www.stellar.org/): financial platform +- [Touch Surgery](https://www.touchsurgery.com/): surgery simulator +- [TrinityCore](https://github.com/TrinityCore/TrinityCore): + open-source MMORPG framework +- [🐙 userver framework](https://userver.tech/): open-source + asynchronous framework with a rich set of abstractions and database + drivers +- [Windows Terminal](https://github.com/microsoft/terminal): the new + Windows terminal [More\...](https://github.com/search?q=fmtlib&type=Code) @@ -442,9 +436,9 @@ arguments. However, it has significant limitations, citing its author: > Three features that have no hope of being accommodated within the > current design are: > -> - Leading zeros (or any other non-space padding) -> - Octal/hexadecimal encoding -> - Runtime width/alignment specification +> - Leading zeros (or any other non-space padding) +> - Octal/hexadecimal encoding +> - Runtime width/alignment specification It is also quite big and has a heavy dependency, STLSoft, which might be too restrictive for using it in some projects. -- cgit v1.2.3 From dee0dbf07f9a992160ad105ec830941b07e69370 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 8 Dec 2023 14:24:11 -0800 Subject: Update README.md --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2e591937..5454ce09 100644 --- a/README.md +++ b/README.md @@ -177,11 +177,11 @@ Output on a modern terminal with Unicode support: | Library | Method | Run Time, s | |-------------------|---------------|-------------| -| libc | printf | > 0.91 | -| libc++ | std::ostream | > 2.49 | -| {fmt} 9.1 | fmt::print | > 0.74 | -| Boost Format 1.80 | boost::format | > 6.26 | -| Folly Format | folly::format | > 1.87 | +| libc | printf | 0.91 | +| libc++ | std::ostream | 2.49 | +| {fmt} 9.1 | fmt::print | 0.74 | +| Boost Format 1.80 | boost::format | 6.26 | +| Folly Format | folly::format | 1.87 | {fmt} is the fastest of the benchmarked methods, \~20% faster than `printf`. @@ -216,12 +216,12 @@ Sierra, best of three) is shown in the following tables. | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | |---------------|-----------------|----------------------|--------------------| -| printf | > 2.6 | > 29 | > 26 | -| printf+string | > 16.4 | > 29 | > 26 | -| iostreams | > 31.1 | > 59 | > 55 | -| {fmt} | > 19.0 | > 37 | > 34 | -| Boost Format | > 91.9 | > 226 | > 203 | -| Folly Format | > 115.7 | > 101 | > 88 | +| printf | 2.6 | 29 | 26 | +| printf+string | 16.4 | 29 | 26 | +| iostreams | 31.1 | 59 | 55 | +| {fmt} | 19.0 | 37 | 34 | +| Boost Format | 91.9 | 226 | 203 | +| Folly Format | 115.7 | 101 | 88 | As you can see, {fmt} has 60% less overhead in terms of resulting binary code size compared to iostreams and comes pretty close to `printf`. @@ -234,12 +234,12 @@ include to measure the overhead of the latter. | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | |---------------|-----------------|----------------------|--------------------| -| printf | > 2.2 | > 33 | > 30 | -| printf+string | > 16.0 | > 33 | > 30 | -| iostreams | > 28.3 | > 56 | > 52 | -| {fmt} | > 18.2 | > 59 | > 50 | -| Boost Format | > 54.1 | > 365 | > 303 | -| Folly Format | > 79.9 | > 445 | > 430 | +| printf | 2.2 | 33 | 30 | +| printf+string | 16.0 | 33 | 30 | +| iostreams | 28.3 | 56 | 52 | +| {fmt} | 18.2 | 59 | 50 | +| Boost Format | 54.1 | 365 | 303 | +| Folly Format | 79.9 | 445 | 430 | `libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries to compare formatting function overhead only. Boost Format is a -- cgit v1.2.3 From 9a6fd11a56fe7d581180651068b8eeb5a81aa9b9 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sun, 10 Dec 2023 01:13:20 +0900 Subject: Fix typo in gmock-gtest-all.cc (#3747) syntetic -> synthetic --- test/gtest/gmock-gtest-all.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gtest/gmock-gtest-all.cc b/test/gtest/gmock-gtest-all.cc index 7b33134f..9d3b9bc1 100644 --- a/test/gtest/gmock-gtest-all.cc +++ b/test/gtest/gmock-gtest-all.cc @@ -1912,7 +1912,7 @@ void AssertHelper::operator=(const Message& message) const { namespace { // When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P -// to creates test cases for it, a syntetic test case is +// to creates test cases for it, a synthetic test case is // inserted to report ether an error or a log message. // // This configuration bit will likely be removed at some point. -- cgit v1.2.3 From 6392dba21c46c3b32a40430ce7d2a24d4b01c6fb Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sat, 9 Dec 2023 21:16:57 +0500 Subject: Fix warning: identifier '_a' preceded by whitespace in a literal operator declaration is deprecated (#3748) Signed-off-by: Vladislav Shchapov --- include/fmt/xchar.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 625ec369..2bf8c276 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -70,7 +70,7 @@ constexpr format_arg_store make_wformat_args( inline namespace literals { #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS -constexpr detail::udl_arg operator"" _a(const wchar_t* s, size_t) { +constexpr detail::udl_arg operator""_a(const wchar_t* s, size_t) { return {s}; } #endif -- cgit v1.2.3 From 640e0c02d48e19076e976b395d919c815a27ae5d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 9 Dec 2023 17:37:07 -0800 Subject: Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5454ce09..41f46abc 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ IEEE754 `float` and `double` formatting than [double-conversion](https://github.com/google/double-conversion) and [ryu](https://github.com/ulfjack/ryu): -[![image](https://user-images.githubusercontent.com/576385/%0A95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html) +[![image](https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html) ## Compile time and code bloat -- cgit v1.2.3 From 9048add999a4e77420fee949f743b9aca0f0bac3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 10 Dec 2023 07:26:45 -0800 Subject: Report out-of-range errors in chrono --- include/fmt/chrono.h | 8 ++++---- test/chrono-test.cc | 17 +++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 57cd0b70..bed584b6 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1049,10 +1049,10 @@ inline void tzset_once() { // Converts value to Int and checks that it's in the range [0, upper). template ::value)> inline Int to_nonnegative_int(T value, Int upper) { - FMT_ASSERT(std::is_unsigned::value || - (value >= 0 && to_unsigned(value) <= to_unsigned(upper)), - "invalid value"); - (void)upper; + if (!std::is_unsigned::value && + (value < 0 || to_unsigned(value) > to_unsigned(upper))) { + FMT_THROW(fmt::format_error("chrono value is out of range")); + } return static_cast(value); } template ::value)> diff --git a/test/chrono-test.cc b/test/chrono-test.cc index cb672816..56fbd382 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -740,8 +740,8 @@ TEST(chrono_test, special_durations) { EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), "03:33:20"); EXPECT_EQ("01.234", - fmt::format("{:.3%S}", std::chrono::duration( - 1.234e12))); + fmt::format("{:.3%S}", + std::chrono::duration(1.234e12))); } TEST(chrono_test, unsigned_duration) { @@ -864,18 +864,18 @@ TEST(chrono_test, timestamps_ratios) { EXPECT_EQ(fmt::format("{:%M:%S}", t1), "01:07.890"); - std::chrono::time_point - t2(std::chrono::minutes(7)); + std::chrono::time_point t2( + std::chrono::minutes(7)); EXPECT_EQ(fmt::format("{:%M:%S}", t2), "07:00"); - std::chrono::time_point>> t3(std::chrono::duration>(7)); EXPECT_EQ(fmt::format("{:%M:%S}", t3), "01:03"); - std::chrono::time_point>> t4(std::chrono::duration>(1)); @@ -1023,3 +1023,8 @@ TEST(chrono_test, glibc_extensions) { EXPECT_EQ(fmt::format("{:%-S}", d), "3.140000"); } } + +TEST(chrono_test, out_of_range) { + auto d = std::chrono::duration(538976288); + EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error); +} \ No newline at end of file -- cgit v1.2.3 From 274ba2645bdae12f6f0c7d7ca24659c4af670548 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Mon, 11 Dec 2023 00:49:53 +0800 Subject: allow format_as() to format reference (#3739) before this change, format_as() is unable to format a type which has `auto format_as() -> const another_type&`, and `another_type` is formattable. because `format_as_result` maps the result type as it is, and the compiler refuses to compile `static_cast(nullptr)`, where T is a reference type. but it would be handy if we could use `format_as()` to format types which, for instance, owns / inherit from a formattable type, and delegate the formatter to these variables instead without creating a copy of them. in this change: * instruct `format_as_result` to map the result type to the decayed type, so that `type` can be the decayed type of result type, and this also enables `type` to be formattable, as long as the decayed type is formattable. * corresponding test is added to format-test.cc Signed-off-by: Kefu Chai --- include/fmt/core.h | 2 +- test/format-test.cc | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index baedccc1..f6e68868 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1332,7 +1332,7 @@ using ulong_type = conditional_t; template struct format_as_result { template ::value || std::is_class::value)> - static auto map(U*) -> decltype(format_as(std::declval())); + static auto map(U*) -> remove_cvref_t()))>; static auto map(...) -> void; using type = decltype(map(static_cast(nullptr))); diff --git a/test/format-test.cc b/test/format-test.cc index 0a9924bf..a708dd00 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2173,6 +2173,13 @@ auto format_as(scoped_enum_as_string) -> std::string { return "foo"; } struct struct_as_int {}; auto format_as(struct_as_int) -> int { return 42; } + +struct struct_as_const_reference { + const std::string name = "foo"; +}; +auto format_as(const struct_as_const_reference& s) -> const std::string& { + return s.name; +} } // namespace test TEST(format_test, format_as) { @@ -2180,6 +2187,7 @@ TEST(format_test, format_as) { EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string_view()), "foo"); EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string()), "foo"); EXPECT_EQ(fmt::format("{}", test::struct_as_int()), "42"); + EXPECT_EQ(fmt::format("{}", test::struct_as_const_reference()), "foo"); } TEST(format_test, format_as_to_string) { -- cgit v1.2.3 From 89860eb9013a345608c8144b1aad5f12b0682d7e Mon Sep 17 00:00:00 2001 From: Mikael Simberg Date: Mon, 11 Dec 2023 18:44:02 +0100 Subject: Use void(*)(void*) instead of decltype(&std::free) to satisfy clang in CUDA mode (#3751) clang can't resolve &std::free in decltype(&std::free) because std::free is overloaded (for host and device). --- include/fmt/std.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index 6f6b23ff..dda3c84c 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -415,7 +415,7 @@ struct formatter< # ifdef FMT_HAS_ABI_CXA_DEMANGLE int status = 0; std::size_t size = 0; - std::unique_ptr demangled_name_ptr( + std::unique_ptr demangled_name_ptr( abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); string_view demangled_name_view; -- cgit v1.2.3 From bbee753579294abbe51659fda8b0df81ae3f6e7e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 13 Dec 2023 06:54:51 -0800 Subject: Make clang-format happy --- include/fmt/format-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 5f8c83a2..bfd00566 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -984,7 +984,7 @@ template <> struct cache_accessor { {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, - {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2} + {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}, #else {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, -- cgit v1.2.3 From 6855bd532b58019d4c43a3bb6fd38653abf31761 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Wed, 13 Dec 2023 22:56:42 +0800 Subject: Apply clang-format * use clang-format v17.0.6 to reformat the tree * tweak some places so that clang-format does not mutate the layout of code too much Signed-off-by: Kefu Chai --- include/fmt/color.h | 7 ++++--- include/fmt/core.h | 3 ++- include/fmt/ostream.h | 2 +- include/fmt/std.h | 10 ++++------ include/fmt/xchar.h | 10 +++++----- src/fmt.cc | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/include/fmt/color.h b/include/fmt/color.h index 57b7f578..34de634c 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -563,9 +563,10 @@ OutputIt vformat_to( fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); \endrst */ -template >::value&& - detail::is_string::value> +template < + typename OutputIt, typename S, typename... Args, + bool enable = detail::is_output_iterator>::value && + detail::is_string::value> inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, Args&&... args) -> typename std::enable_if::type { diff --git a/include/fmt/core.h b/include/fmt/core.h index f6e68868..9bfee019 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2429,7 +2429,8 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( case 'G': return parse_presentation_type(pres::general_upper, float_set); case 'c': - if (arg_type == type::bool_type) throw_format_error("invalid format specifier"); + if (arg_type == type::bool_type) + throw_format_error("invalid format specifier"); return parse_presentation_type(pres::chr, integral_set); case 's': return parse_presentation_type(pres::string, diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 2ed35df1..3086191a 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -95,7 +95,7 @@ void format_value(buffer& buf, const T& value) { auto&& format_buf = formatbuf>(buf); auto&& output = std::basic_ostream(&format_buf); #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) - output.imbue(std::locale::classic()); // The default is always unlocalized. + output.imbue(std::locale::classic()); // The default is always unlocalized. #endif output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); diff --git a/include/fmt/std.h b/include/fmt/std.h index dda3c84c..5bafcefb 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -137,8 +137,7 @@ template struct formatter { debug_ = true; ++it; } - if (it != end && (*it == 'g' || *it == 'n')) - path_type_ = *it++; + if (it != end && (*it == 'g' || *it == 'n')) path_type_ = *it++; return it; } @@ -246,8 +245,7 @@ FMT_END_NAMESPACE #ifdef __cpp_lib_source_location FMT_BEGIN_NAMESPACE FMT_EXPORT -template<> -struct formatter { +template <> struct formatter { template FMT_CONSTEXPR auto parse(ParseContext& ctx) { return ctx.begin(); } @@ -384,7 +382,7 @@ template struct formatter { FMT_EXPORT template struct formatter< - T, Char, // DEPRECATED! Mixing code unit types. + T, Char, // DEPRECATED! Mixing code unit types. typename std::enable_if::value>::type> { private: bool with_typename_ = false; @@ -415,7 +413,7 @@ struct formatter< # ifdef FMT_HAS_ABI_CXA_DEMANGLE int status = 0; std::size_t size = 0; - std::unique_ptr demangled_name_ptr( + std::unique_ptr demangled_name_ptr( abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); string_view demangled_name_view; diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 2bf8c276..9a7aa4d7 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -172,11 +172,11 @@ inline auto vformat_to( return detail::get_iterator(buf, out); } -template < - typename OutputIt, typename Locale, typename S, typename... T, - typename Char = char_t, - bool enable = detail::is_output_iterator::value&& - detail::is_locale::value&& detail::is_exotic_char::value> +template , + bool enable = detail::is_output_iterator::value && + detail::is_locale::value && + detail::is_exotic_char::value> inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, T&&... args) -> typename std::enable_if::type { diff --git a/src/fmt.cc b/src/fmt.cc index 6638bb4a..5330463a 100644 --- a/src/fmt.cc +++ b/src/fmt.cc @@ -101,7 +101,7 @@ extern "C++" { // gcc doesn't yet implement private module fragments #if !FMT_GCC_VERSION -module : private; +module :private; #endif #include "format.cc" -- cgit v1.2.3 From 7d757cba5dc075e52ea66c725c3ce6c414fc01a4 Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Wed, 13 Dec 2023 23:20:23 +0800 Subject: CI: add lint github workflow for running clang-format (#3749) so that we can identify changes which do not confirm to the clang-format rules. Signed-off-by: Kefu Chai --- .github/workflows/lint.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..b906fac2 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,26 @@ +name: lint + +on: + pull_request: + paths: + - '**.h' + - '**.cc' + +permissions: + contents: read + +jobs: + format_code: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install clang-format + uses: aminya/setup-cpp@v1 + with: + clangformat: 17.0.5 + + - name: Run clang-format + run: | + find include src -name '*.h' -o -name '*.cc' | xargs clang-format -i -style=file -fallback-style=none + git diff --exit-code -- cgit v1.2.3 From 5471a2426c432503bdadeab0c03df387bea97463 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 13 Dec 2023 09:32:00 -0800 Subject: Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41f46abc..697a6efa 100644 --- a/README.md +++ b/README.md @@ -277,12 +277,14 @@ check that is capable of converting occurrences of `printf` and `fprintf` to `fmt::print` if configured to do so. (By default it converts to `std::print`.) -# Projects using this library +# Notable projects using this library - [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform real-time strategy game - [AMPL/MP](https://github.com/ampl/mp): an open-source library for mathematical programming +- [Apple's FoundationDB](https://github.com/apple/foundationdb): an open-source, + distributed, transactional key-value store - [Aseprite](https://github.com/aseprite/aseprite): animated sprite editor & pixel art tool - [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft -- cgit v1.2.3 From 6025bd7c37aeeca982ca2a9151d9002222c5b72e Mon Sep 17 00:00:00 2001 From: js324 Date: Fri, 15 Dec 2023 10:51:25 -0500 Subject: Add localized formatting to non-decimal presentation types of ints (#3750) --- include/fmt/format.h | 69 ++++++++++++++++++++++++++++++++++++++++------------ test/format-test.cc | 8 ++++++ 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 79bf5256..fa76aa11 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2113,24 +2113,66 @@ template class digit_grouping { } }; +FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { + prefix |= prefix != 0 ? value << 8 : value; + prefix += (1u + (value > 0xff ? 1 : 0)) << 24; +} + // Writes a decimal integer with digit grouping. template auto write_int(OutputIt out, UInt value, unsigned prefix, const format_specs& specs, const digit_grouping& grouping) -> OutputIt { - static_assert(std::is_same, UInt>::value, ""); - int num_digits = count_digits(value); - char digits[40]; - format_decimal(digits, value, num_digits); - unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + - grouping.count_separators(num_digits)); + static_assert(std::is_same, UInt>::value, ""); + int num_digits = 0; + auto buffer = memory_buffer(); + switch (specs.type) { + case presentation_type::none: + case presentation_type::dec: { + num_digits = count_digits(value); + format_decimal(appender(buffer), value, num_digits); + break; + } + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); + num_digits = count_digits<4>(value); + format_uint<4,Char>(appender(buffer), value, num_digits, upper); + break; + } + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); + num_digits = count_digits<1>(value); + format_uint<1,Char>(appender(buffer), value, num_digits); + break; + } + case presentation_type::oct: { + num_digits = count_digits<3>(value); + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && value != 0) + prefix_append(prefix, '0'); + format_uint<3,Char>(appender(buffer), value, num_digits); + break; + } + case presentation_type::chr: + return write_char(out, static_cast(value), specs); + default: + throw_format_error("invalid format specifier"); + } + + unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + + to_unsigned(grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - if (prefix != 0) { - char sign = static_cast(prefix); - *it++ = static_cast(sign); - } - return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); + return grouping.apply(it, string_view(buffer.data(), buffer.size())); }); } @@ -2143,11 +2185,6 @@ inline auto write_loc(OutputIt, loc_value, const format_specs&, return false; } -FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { - prefix |= prefix != 0 ? value << 8 : value; - prefix += (1u + (value > 0xff ? 1 : 0)) << 24; -} - template struct write_int_arg { UInt abs_value; unsigned prefix; diff --git a/test/format-test.cc b/test/format-test.cc index a708dd00..e967ed32 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2297,6 +2297,14 @@ TEST(format_test, format_named_arg_with_locale) { "42"); } +TEST(format_test, format_locale) { + auto loc = + std::locale({}, new fmt::format_facet(",")); + EXPECT_EQ("7,5bc,d15", fmt::format(loc, "{:Lx}", 123456789)); + EXPECT_EQ("-0b111,010,110,111,100,110,100,010,101", fmt::format(loc, "{:#Lb}", -123456789)); + EXPECT_EQ(" 30,071", fmt::format(loc, "{:10Lo}", 12345)); +} + #endif // FMT_STATIC_THOUSANDS_SEPARATOR struct convertible_to_nonconst_cstring { -- cgit v1.2.3 From afa85e46c3fd34c377a46672b57f8a50caa5b38a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 17 Dec 2023 07:32:22 -0800 Subject: Apply clang-format --- include/fmt/format.h | 14 +++++++------- test/format-test.cc | 16 +++++++--------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index fa76aa11..7e1e6926 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2123,7 +2123,7 @@ template auto write_int(OutputIt out, UInt value, unsigned prefix, const format_specs& specs, const digit_grouping& grouping) -> OutputIt { - static_assert(std::is_same, UInt>::value, ""); + static_assert(std::is_same, UInt>::value, ""); int num_digits = 0; auto buffer = memory_buffer(); switch (specs.type) { @@ -2139,7 +2139,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, if (specs.alt) prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); - format_uint<4,Char>(appender(buffer), value, num_digits, upper); + format_uint<4, Char>(appender(buffer), value, num_digits, upper); break; } case presentation_type::bin_lower: @@ -2148,7 +2148,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, if (specs.alt) prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); num_digits = count_digits<1>(value); - format_uint<1,Char>(appender(buffer), value, num_digits); + format_uint<1, Char>(appender(buffer), value, num_digits); break; } case presentation_type::oct: { @@ -2157,7 +2157,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // is not greater than the number of digits. if (specs.alt && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); - format_uint<3,Char>(appender(buffer), value, num_digits); + format_uint<3, Char>(appender(buffer), value, num_digits); break; } case presentation_type::chr: @@ -2167,11 +2167,11 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, } unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) + - to_unsigned(grouping.count_separators(num_digits)); + to_unsigned(grouping.count_separators(num_digits)); return write_padded( out, specs, size, size, [&](reserve_iterator it) { - for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) - *it++ = static_cast(p & 0xff); + for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8) + *it++ = static_cast(p & 0xff); return grouping.apply(it, string_view(buffer.data(), buffer.size())); }); } diff --git a/test/format-test.cc b/test/format-test.cc index e967ed32..6cd2b5ec 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1545,12 +1545,10 @@ TEST(format_test, format_cstring) { char nonconst[] = "nonconst"; EXPECT_EQ("nonconst", fmt::format("{0}", nonconst)); auto nullstr = static_cast(nullptr); - EXPECT_THROW_MSG( - (void)fmt::format("{}", nullstr), - format_error, "string pointer is null"); - EXPECT_THROW_MSG( - (void)fmt::format("{:s}", nullstr), - format_error, "string pointer is null"); + EXPECT_THROW_MSG((void)fmt::format("{}", nullstr), format_error, + "string pointer is null"); + EXPECT_THROW_MSG((void)fmt::format("{:s}", nullstr), format_error, + "string pointer is null"); } void function_pointer_test(int, double, std::string) {} @@ -2298,10 +2296,10 @@ TEST(format_test, format_named_arg_with_locale) { } TEST(format_test, format_locale) { - auto loc = - std::locale({}, new fmt::format_facet(",")); + auto loc = std::locale({}, new fmt::format_facet(",")); EXPECT_EQ("7,5bc,d15", fmt::format(loc, "{:Lx}", 123456789)); - EXPECT_EQ("-0b111,010,110,111,100,110,100,010,101", fmt::format(loc, "{:#Lb}", -123456789)); + EXPECT_EQ("-0b111,010,110,111,100,110,100,010,101", + fmt::format(loc, "{:#Lb}", -123456789)); EXPECT_EQ(" 30,071", fmt::format(loc, "{:10Lo}", 12345)); } -- cgit v1.2.3 From 923005bd4fe6c4bec48db372dcefe6c90c9822bc Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sun, 17 Dec 2023 21:33:17 +0500 Subject: Add stdlib version check for C++20 (#3754) Signed-off-by: Vladislav Shchapov --- include/fmt/core.h | 9 ++++++--- test/compile-test.cc | 15 +++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 9bfee019..f2e03b7c 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -105,9 +105,12 @@ # define FMT_CONSTEXPR #endif -#if ((FMT_CPLUSPLUS >= 202002L) && \ - (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +#if (FMT_CPLUSPLUS >= 202002L || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \ + ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \ + (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \ + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1928)) && \ + defined(__cpp_lib_is_constant_evaluated) # define FMT_CONSTEXPR20 constexpr #else # define FMT_CONSTEXPR20 diff --git a/test/compile-test.cc b/test/compile-test.cc index d6c7c643..8551303e 100644 --- a/test/compile-test.cc +++ b/test/compile-test.cc @@ -280,15 +280,18 @@ TEST(compile_test, compile_format_string_literal) { #endif // MSVS 2019 19.29.30145.0 - Support C++20 and OK. -// MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal -// compiler error. +// MSVS 2022 19.32.31332.0, 19.37.32826.1 - compile-test.cc(362,3): fatal error +// C1001: Internal compiler error. // (compiler file // 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp', // line 8635) -#if ((FMT_CPLUSPLUS >= 202002L) && \ - (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9) && \ - (!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \ - (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002) +#if (FMT_CPLUSPLUS >= 202002L || \ + (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \ + ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \ + (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \ + (!FMT_MSC_VERSION || \ + (FMT_MSC_VERSION >= 1928 && FMT_MSC_VERSION < 1930))) && \ + defined(__cpp_lib_is_constant_evaluated) template struct test_string { template constexpr bool operator==(const T& rhs) const noexcept { return fmt::basic_string_view(rhs).compare(buffer) == 0; -- cgit v1.2.3 From b8f81dede502a4b6dbcd1787d6a35008e8d3e5be Mon Sep 17 00:00:00 2001 From: StepSecurity Bot Date: Sun, 17 Dec 2023 17:01:17 -0800 Subject: [StepSecurity] ci: Harden GitHub Actions (#3759) Signed-off-by: StepSecurity Bot --- .github/workflows/cifuzz.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index f87899d0..ae2d6b2a 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -10,13 +10,13 @@ jobs: steps: - name: Build Fuzzers id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@061583ebb5a96653e42feb3a97ee513eedc18078 # master with: oss-fuzz-project-name: 'fmt' dry-run: false language: c++ - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@061583ebb5a96653e42feb3a97ee513eedc18078 # master with: oss-fuzz-project-name: 'fmt' fuzz-seconds: 300 -- cgit v1.2.3 From 9165434e5a3c0b2fc5dacfff87771a9638b5c94a Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Tue, 19 Dec 2023 02:00:11 +0500 Subject: Enable consteval in MSVC VS2019 version 16.10 (#3757) Signed-off-by: Vladislav Shchapov --- include/fmt/core.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index f2e03b7c..3b5bd9ae 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -227,8 +227,9 @@ __apple_build_version__ >= 14000029L) && \ FMT_CPLUSPLUS >= 202002L) || \ (defined(__cpp_consteval) && \ - (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) -// consteval is broken in MSVC before VS2022 and Apple clang before 14. + (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1929)) +// consteval is broken in MSVC before VS2019 version 16.10 and Apple clang +// before 14. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL # else -- cgit v1.2.3 From 6b07fff0d906edc0d42ab8114f647f42f1183c5c Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Wed, 20 Dec 2023 00:03:06 +0500 Subject: Make hex float test more stable on different libc (#3762) Signed-off-by: Vladislav Shchapov --- test/format-test.cc | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/test/format-test.cc b/test/format-test.cc index 6cd2b5ec..d6802f79 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1361,14 +1361,11 @@ TEST(format_test, format_double) { EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02"); EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02"); EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6"); - char buffer[buffer_size]; #if FMT_CPLUSPLUS >= 201703L double xd = 0x1.ffffffffffp+2; - safe_sprintf(buffer, "%.*a", 10, xd); - EXPECT_EQ(fmt::format("{:.10a}", xd), buffer); - safe_sprintf(buffer, "%.*a", 9, xd); - EXPECT_EQ(fmt::format("{:.9a}", xd), buffer); + EXPECT_EQ(fmt::format("{:.10a}", xd), "0x1.ffffffffffp+2"); + EXPECT_EQ(fmt::format("{:.9a}", xd), "0x2.000000000p+2"); if (std::numeric_limits::digits == 64) { auto ld = 0xf.ffffffffffp-3l; @@ -1384,8 +1381,7 @@ TEST(format_test, format_double) { EXPECT_EQ(fmt::format("{:#a}", d), "0x1.p-1022"); d = (std::numeric_limits::max)(); - safe_sprintf(buffer, "%a", d); - EXPECT_EQ(fmt::format("{:a}", d), buffer); + EXPECT_EQ(fmt::format("{:a}", d), "0x1.fffffffffffffp+1023"); d = std::numeric_limits::denorm_min(); EXPECT_EQ(fmt::format("{:a}", d), "0x0.0000000000001p-1022"); @@ -1402,8 +1398,7 @@ TEST(format_test, format_double) { EXPECT_EQ(fmt::format("{:a}", ld), "0x0.000000000000001p-16382"); } - safe_sprintf(buffer, "%.*a", 10, 4.2); - EXPECT_EQ(fmt::format("{:.10a}", 4.2), buffer); + EXPECT_EQ(fmt::format("{:.10a}", 4.2), "0x1.0ccccccccdp+2"); EXPECT_EQ(fmt::format("{:a}", -42.0), "-0x1.5p+5"); EXPECT_EQ(fmt::format("{:A}", -42.0), "-0X1.5P+5"); -- cgit v1.2.3 From 18c43a214c8f67112678ae1053cb179972948b4c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 19 Dec 2023 13:46:48 -0800 Subject: Cleanup test --- test/format-test.cc | 19 ++++++++----------- test/util.h | 26 ++++++++++++++------------ 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/test/format-test.cc b/test/format-test.cc index d6802f79..a9dd41a2 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -499,20 +499,17 @@ TEST(format_test, arg_errors) { EXPECT_THROW_MSG((void)fmt::format(runtime("{00}"), 42), format_error, "invalid format string"); - char format_str[buffer_size]; - safe_sprintf(format_str, "{%u", INT_MAX); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + auto int_max = std::to_string(INT_MAX); + EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_max)), format_error, "invalid format string"); - safe_sprintf(format_str, "{%u}", INT_MAX); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, - "argument not found"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_max + "}")), + format_error, "argument not found"); - safe_sprintf(format_str, "{%u", INT_MAX + 1u); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, + auto int_maxer = std::to_string(INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_maxer)), format_error, "invalid format string"); - safe_sprintf(format_str, "{%u}", INT_MAX + 1u); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error, - "argument not found"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_maxer + "}")), + format_error, "argument not found"); } template struct test_format { diff --git a/test/util.h b/test/util.h index 9120e22e..803cdeea 100644 --- a/test/util.h +++ b/test/util.h @@ -29,9 +29,9 @@ void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) { extern const char* const file_content; // Opens a buffered file for reading. -fmt::buffered_file open_buffered_file(FILE** fp = nullptr); +auto open_buffered_file(FILE** fp = nullptr) -> fmt::buffered_file; -inline FILE* safe_fopen(const char* filename, const char* mode) { +inline auto safe_fopen(const char* filename, const char* mode) -> FILE* { #if defined(_WIN32) && !defined(__MINGW32__) // Fix MSVC warning about "unsafe" fopen. FILE* f = nullptr; @@ -51,17 +51,17 @@ template class basic_test_string { public: explicit basic_test_string(const Char* value = empty) : value_(value) {} - const std::basic_string& value() const { return value_; } + auto value() const -> const std::basic_string& { return value_; } }; template const Char basic_test_string::empty[] = {0}; -typedef basic_test_string test_string; -typedef basic_test_string test_wstring; +using test_string = basic_test_string; +using test_wstring = basic_test_string; template -std::basic_ostream& operator<<(std::basic_ostream& os, - const basic_test_string& s) { +auto operator<<(std::basic_ostream& os, const basic_test_string& s) + -> std::basic_ostream& { os << s.value(); return os; } @@ -72,10 +72,12 @@ class date { public: date(int year, int month, int day) : year_(year), month_(month), day_(day) {} - int year() const { return year_; } - int month() const { return month_; } - int day() const { return day_; } + auto year() const -> int { return year_; } + auto month() const -> int { return month_; } + auto day() const -> int { return day_; } }; -// Returns a locale with the given name if available or classic locale otherwise. -std::locale get_locale(const char* name, const char* alt_name = nullptr); +// Returns a locale with the given name if available or classic locale +// otherwise. +auto get_locale(const char* name, const char* alt_name = nullptr) + -> std::locale; -- cgit v1.2.3 From 3a2c50d4ac462d1d526d80f90e2cc3d8adf73e0a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 19 Dec 2023 14:59:00 -0800 Subject: Cleanup test --- test/format-test.cc | 813 +++++++++++++++++++++++++--------------------------- 1 file changed, 397 insertions(+), 416 deletions(-) diff --git a/test/format-test.cc b/test/format-test.cc index a9dd41a2..0f7fb008 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -452,18 +452,18 @@ TEST(format_test, exception_from_lib) { } TEST(format_test, escape) { - EXPECT_EQ("{", fmt::format("{{")); - EXPECT_EQ("before {", fmt::format("before {{")); - EXPECT_EQ("{ after", fmt::format("{{ after")); - EXPECT_EQ("before { after", fmt::format("before {{ after")); + EXPECT_EQ(fmt::format("{{"), "{"); + EXPECT_EQ(fmt::format("before {{"), "before {"); + EXPECT_EQ(fmt::format("{{ after"), "{ after"); + EXPECT_EQ(fmt::format("before {{ after"), "before { after"); - EXPECT_EQ("}", fmt::format("}}")); - EXPECT_EQ("before }", fmt::format("before }}")); - EXPECT_EQ("} after", fmt::format("}} after")); - EXPECT_EQ("before } after", fmt::format("before }} after")); + EXPECT_EQ(fmt::format("}}"), "}"); + EXPECT_EQ(fmt::format("before }}"), "before }"); + EXPECT_EQ(fmt::format("}} after"), "} after"); + EXPECT_EQ(fmt::format("before }} after"), "before } after"); - EXPECT_EQ("{}", fmt::format("{{}}")); - EXPECT_EQ("{42}", fmt::format("{{{0}}}", 42)); + EXPECT_EQ(fmt::format("{{}}"), "{}"); + EXPECT_EQ(fmt::format("{{{0}}}", 42), "{42}"); } TEST(format_test, unmatched_braces) { @@ -475,16 +475,16 @@ TEST(format_test, unmatched_braces) { "invalid format string"); } -TEST(format_test, no_args) { EXPECT_EQ("test", fmt::format("test")); } +TEST(format_test, no_args) { EXPECT_EQ(fmt::format("test"), "test"); } TEST(format_test, args_in_different_positions) { - EXPECT_EQ("42", fmt::format("{0}", 42)); - EXPECT_EQ("before 42", fmt::format("before {0}", 42)); - EXPECT_EQ("42 after", fmt::format("{0} after", 42)); - EXPECT_EQ("before 42 after", fmt::format("before {0} after", 42)); - EXPECT_EQ("answer = 42", fmt::format("{0} = {1}", "answer", 42)); - EXPECT_EQ("42 is the answer", fmt::format("{1} is the {0}", "answer", 42)); - EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad")); + EXPECT_EQ(fmt::format("{0}", 42), "42"); + EXPECT_EQ(fmt::format("before {0}", 42), "before 42"); + EXPECT_EQ(fmt::format("{0} after", 42), "42 after"); + EXPECT_EQ(fmt::format("before {0} after", 42), "before 42 after"); + EXPECT_EQ(fmt::format("{0} = {1}", "answer", 42), "answer = 42"); + EXPECT_EQ(fmt::format("{1} is the {0}", "answer", 42), "42 is the answer"); + EXPECT_EQ(fmt::format("{0}{1}{0}", "abra", "cad"), "abracadabra"); } TEST(format_test, arg_errors) { @@ -514,14 +514,14 @@ TEST(format_test, arg_errors) { template struct test_format { template - static std::string format(fmt::string_view fmt, const T&... args) { + static auto format(fmt::string_view fmt, const T&... args) -> std::string { return test_format::format(fmt, N - 1, args...); } }; template <> struct test_format<0> { template - static std::string format(fmt::string_view fmt, const T&... args) { + static auto format(fmt::string_view fmt, const T&... args) -> std::string { return fmt::format(runtime(fmt), args...); } }; @@ -541,10 +541,10 @@ TEST(format_test, many_args) { TEST(format_test, named_arg) { EXPECT_EQ("1/a/A", fmt::format("{_1}/{a_}/{A_}", fmt::arg("a_", 'a'), fmt::arg("A_", "A"), fmt::arg("_1", 1))); - EXPECT_EQ(" -42", fmt::format("{0:{width}}", -42, fmt::arg("width", 4))); + EXPECT_EQ(fmt::format("{0:{width}}", -42, fmt::arg("width", 4)), " -42"); EXPECT_EQ("st", fmt::format("{0:.{precision}}", "str", fmt::arg("precision", 2))); - EXPECT_EQ("1 2", fmt::format("{} {two}", 1, fmt::arg("two", 2))); + EXPECT_EQ(fmt::format("{} {two}", 1, fmt::arg("two", 2)), "1 2"); EXPECT_EQ("42", fmt::format("{c}", fmt::arg("a", 0), fmt::arg("b", 0), fmt::arg("c", 42), fmt::arg("d", 0), fmt::arg("e", 0), @@ -559,12 +559,12 @@ TEST(format_test, named_arg) { } TEST(format_test, auto_arg_index) { - EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c')); + EXPECT_EQ(fmt::format("{}{}{}", 'a', 'b', 'c'), "abc"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0}{}"), 'a', 'b'), format_error, "cannot switch from manual to automatic argument indexing"); EXPECT_THROW_MSG((void)fmt::format(runtime("{}{0}"), 'a', 'b'), format_error, "cannot switch from automatic to manual argument indexing"); - EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2)); + EXPECT_EQ(fmt::format("{:.{}}", 1.2345, 2), "1.2"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0}:.{}"), 1.2345, 2), format_error, "cannot switch from manual to automatic argument indexing"); @@ -575,57 +575,57 @@ TEST(format_test, auto_arg_index) { "argument not found"); } -TEST(format_test, empty_specs) { EXPECT_EQ("42", fmt::format("{0:}", 42)); } +TEST(format_test, empty_specs) { EXPECT_EQ(fmt::format("{0:}", 42), "42"); } TEST(format_test, left_align) { - EXPECT_EQ("42 ", fmt::format("{0:<4}", 42)); - EXPECT_EQ("42 ", fmt::format("{0:<4o}", 042)); - EXPECT_EQ("42 ", fmt::format("{0:<4x}", 0x42)); - EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42)); - EXPECT_EQ("42 ", fmt::format("{0:<5}", 42u)); - EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42l)); - EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ul)); - EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42ll)); - EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ull)); - EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0)); - EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0l)); - EXPECT_EQ("c ", fmt::format("{0:<5}", 'c')); - EXPECT_EQ("abc ", fmt::format("{0:<5}", "abc")); - EXPECT_EQ("0xface ", fmt::format("{0:<8}", reinterpret_cast(0xface))); + EXPECT_EQ(fmt::format("{0:<4}", 42), "42 "); + EXPECT_EQ(fmt::format("{0:<4o}", 042), "42 "); + EXPECT_EQ(fmt::format("{0:<4x}", 0x42), "42 "); + EXPECT_EQ(fmt::format("{0:<5}", -42), "-42 "); + EXPECT_EQ(fmt::format("{0:<5}", 42u), "42 "); + EXPECT_EQ(fmt::format("{0:<5}", -42l), "-42 "); + EXPECT_EQ(fmt::format("{0:<5}", 42ul), "42 "); + EXPECT_EQ(fmt::format("{0:<5}", -42ll), "-42 "); + EXPECT_EQ(fmt::format("{0:<5}", 42ull), "42 "); + EXPECT_EQ(fmt::format("{0:<5}", -42.0), "-42 "); + EXPECT_EQ(fmt::format("{0:<5}", -42.0l), "-42 "); + EXPECT_EQ(fmt::format("{0:<5}", 'c'), "c "); + EXPECT_EQ(fmt::format("{0:<5}", "abc"), "abc "); + EXPECT_EQ(fmt::format("{0:<8}", reinterpret_cast(0xface)), "0xface "); } TEST(format_test, right_align) { - EXPECT_EQ(" 42", fmt::format("{0:>4}", 42)); - EXPECT_EQ(" 42", fmt::format("{0:>4o}", 042)); - EXPECT_EQ(" 42", fmt::format("{0:>4x}", 0x42)); - EXPECT_EQ(" -42", fmt::format("{0:>5}", -42)); - EXPECT_EQ(" 42", fmt::format("{0:>5}", 42u)); - EXPECT_EQ(" -42", fmt::format("{0:>5}", -42l)); - EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ul)); - EXPECT_EQ(" -42", fmt::format("{0:>5}", -42ll)); - EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ull)); - EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0)); - EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0l)); - EXPECT_EQ(" c", fmt::format("{0:>5}", 'c')); - EXPECT_EQ(" abc", fmt::format("{0:>5}", "abc")); - EXPECT_EQ(" 0xface", fmt::format("{0:>8}", reinterpret_cast(0xface))); + EXPECT_EQ(fmt::format("{0:>4}", 42), " 42"); + EXPECT_EQ(fmt::format("{0:>4o}", 042), " 42"); + EXPECT_EQ(fmt::format("{0:>4x}", 0x42), " 42"); + EXPECT_EQ(fmt::format("{0:>5}", -42), " -42"); + EXPECT_EQ(fmt::format("{0:>5}", 42u), " 42"); + EXPECT_EQ(fmt::format("{0:>5}", -42l), " -42"); + EXPECT_EQ(fmt::format("{0:>5}", 42ul), " 42"); + EXPECT_EQ(fmt::format("{0:>5}", -42ll), " -42"); + EXPECT_EQ(fmt::format("{0:>5}", 42ull), " 42"); + EXPECT_EQ(fmt::format("{0:>5}", -42.0), " -42"); + EXPECT_EQ(fmt::format("{0:>5}", -42.0l), " -42"); + EXPECT_EQ(fmt::format("{0:>5}", 'c'), " c"); + EXPECT_EQ(fmt::format("{0:>5}", "abc"), " abc"); + EXPECT_EQ(fmt::format("{0:>8}", reinterpret_cast(0xface)), " 0xface"); } TEST(format_test, center_align) { - EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42)); - EXPECT_EQ(" 42 ", fmt::format("{0:^5o}", 042)); - EXPECT_EQ(" 42 ", fmt::format("{0:^5x}", 0x42)); - EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42)); - EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42u)); - EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42l)); - EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ul)); - EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42ll)); - EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ull)); - EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0)); - EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0l)); - EXPECT_EQ(" c ", fmt::format("{0:^5}", 'c')); - EXPECT_EQ(" abc ", fmt::format("{0:^6}", "abc")); - EXPECT_EQ(" 0xface ", fmt::format("{0:^8}", reinterpret_cast(0xface))); + EXPECT_EQ(fmt::format("{0:^5}", 42), " 42 "); + EXPECT_EQ(fmt::format("{0:^5o}", 042), " 42 "); + EXPECT_EQ(fmt::format("{0:^5x}", 0x42), " 42 "); + EXPECT_EQ(fmt::format("{0:^5}", -42), " -42 "); + EXPECT_EQ(fmt::format("{0:^5}", 42u), " 42 "); + EXPECT_EQ(fmt::format("{0:^5}", -42l), " -42 "); + EXPECT_EQ(fmt::format("{0:^5}", 42ul), " 42 "); + EXPECT_EQ(fmt::format("{0:^5}", -42ll), " -42 "); + EXPECT_EQ(fmt::format("{0:^5}", 42ull), " 42 "); + EXPECT_EQ(fmt::format("{0:^5}", -42.0), " -42 "); + EXPECT_EQ(fmt::format("{0:^5}", -42.0l), " -42 "); + EXPECT_EQ(fmt::format("{0:^5}", 'c'), " c "); + EXPECT_EQ(fmt::format("{0:^6}", "abc"), " abc "); + EXPECT_EQ(fmt::format("{0:^8}", reinterpret_cast(0xface)), " 0xface "); } TEST(format_test, fill) { @@ -633,44 +633,44 @@ TEST(format_test, fill) { "invalid fill character '{'"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}}"), 'c'), format_error, "invalid fill character '{'"); - EXPECT_EQ("**42", fmt::format("{0:*>4}", 42)); - EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42)); - EXPECT_EQ("***42", fmt::format("{0:*>5}", 42u)); - EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42l)); - EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ul)); - EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42ll)); - EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ull)); - EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0)); - EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0l)); - EXPECT_EQ("c****", fmt::format("{0:*<5}", 'c')); - EXPECT_EQ("abc**", fmt::format("{0:*<5}", "abc")); + EXPECT_EQ(fmt::format("{0:*>4}", 42), "**42"); + EXPECT_EQ(fmt::format("{0:*>5}", -42), "**-42"); + EXPECT_EQ(fmt::format("{0:*>5}", 42u), "***42"); + EXPECT_EQ(fmt::format("{0:*>5}", -42l), "**-42"); + EXPECT_EQ(fmt::format("{0:*>5}", 42ul), "***42"); + EXPECT_EQ(fmt::format("{0:*>5}", -42ll), "**-42"); + EXPECT_EQ(fmt::format("{0:*>5}", 42ull), "***42"); + EXPECT_EQ(fmt::format("{0:*>5}", -42.0), "**-42"); + EXPECT_EQ(fmt::format("{0:*>5}", -42.0l), "**-42"); + EXPECT_EQ(fmt::format("{0:*<5}", 'c'), "c****"); + EXPECT_EQ(fmt::format("{0:*<5}", "abc"), "abc**"); EXPECT_EQ("**0xface", fmt::format("{0:*>8}", reinterpret_cast(0xface))); - EXPECT_EQ("foo=", fmt::format("{:}=", "foo")); + EXPECT_EQ(fmt::format("{:}=", "foo"), "foo="); EXPECT_EQ(std::string("\0\0\0*", 4), fmt::format(string_view("{:\0>4}", 6), '*')); - EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42)); + EXPECT_EQ(fmt::format("{0:ж>4}", 42), "жж42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0), format_error, "invalid format specifier"); } TEST(format_test, plus_sign) { - EXPECT_EQ("+42", fmt::format("{0:+}", 42)); - EXPECT_EQ("-42", fmt::format("{0:+}", -42)); - EXPECT_EQ("+42", fmt::format("{0:+}", 42)); + EXPECT_EQ(fmt::format("{0:+}", 42), "+42"); + EXPECT_EQ(fmt::format("{0:+}", -42), "-42"); + EXPECT_EQ(fmt::format("{0:+}", 42), "+42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error, "invalid format specifier"); - EXPECT_EQ("+42", fmt::format("{0:+}", 42l)); + EXPECT_EQ(fmt::format("{0:+}", 42l), "+42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error, "invalid format specifier"); - EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); + EXPECT_EQ(fmt::format("{0:+}", 42ll), "+42"); #if FMT_USE_INT128 - EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42))); + EXPECT_EQ(fmt::format("{0:+}", __int128_t(42)), "+42"); #endif EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error, "invalid format specifier"); - EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); - EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); + EXPECT_EQ(fmt::format("{0:+}", 42.0), "+42"); + EXPECT_EQ(fmt::format("{0:+}", 42.0l), "+42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error, @@ -681,19 +681,19 @@ TEST(format_test, plus_sign) { } TEST(format_test, minus_sign) { - EXPECT_EQ("42", fmt::format("{0:-}", 42)); - EXPECT_EQ("-42", fmt::format("{0:-}", -42)); - EXPECT_EQ("42", fmt::format("{0:-}", 42)); + EXPECT_EQ(fmt::format("{0:-}", 42), "42"); + EXPECT_EQ(fmt::format("{0:-}", -42), "-42"); + EXPECT_EQ(fmt::format("{0:-}", 42), "42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error, "invalid format specifier"); - EXPECT_EQ("42", fmt::format("{0:-}", 42l)); + EXPECT_EQ(fmt::format("{0:-}", 42l), "42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error, "invalid format specifier"); - EXPECT_EQ("42", fmt::format("{0:-}", 42ll)); + EXPECT_EQ(fmt::format("{0:-}", 42ll), "42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error, "invalid format specifier"); - EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); - EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); + EXPECT_EQ(fmt::format("{0:-}", 42.0), "42"); + EXPECT_EQ(fmt::format("{0:-}", 42.0l), "42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error, @@ -704,19 +704,19 @@ TEST(format_test, minus_sign) { } TEST(format_test, space_sign) { - EXPECT_EQ(" 42", fmt::format("{0: }", 42)); - EXPECT_EQ("-42", fmt::format("{0: }", -42)); - EXPECT_EQ(" 42", fmt::format("{0: }", 42)); + EXPECT_EQ(fmt::format("{0: }", 42), " 42"); + EXPECT_EQ(fmt::format("{0: }", -42), "-42"); + EXPECT_EQ(fmt::format("{0: }", 42), " 42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error, "invalid format specifier"); - EXPECT_EQ(" 42", fmt::format("{0: }", 42l)); + EXPECT_EQ(fmt::format("{0: }", 42l), " 42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error, "invalid format specifier"); - EXPECT_EQ(" 42", fmt::format("{0: }", 42ll)); + EXPECT_EQ(fmt::format("{0: }", 42ll), " 42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error, "invalid format specifier"); - EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); - EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); + EXPECT_EQ(fmt::format("{0: }", 42.0), " 42"); + EXPECT_EQ(fmt::format("{0: }", 42.0l), " 42"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error, @@ -727,45 +727,45 @@ TEST(format_test, space_sign) { } TEST(format_test, hash_flag) { - EXPECT_EQ("42", fmt::format("{0:#}", 42)); - EXPECT_EQ("-42", fmt::format("{0:#}", -42)); - EXPECT_EQ("0b101010", fmt::format("{0:#b}", 42)); - EXPECT_EQ("0B101010", fmt::format("{0:#B}", 42)); - EXPECT_EQ("-0b101010", fmt::format("{0:#b}", -42)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42)); - EXPECT_EQ("0X42", fmt::format("{0:#X}", 0x42)); - EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42)); - EXPECT_EQ("0", fmt::format("{0:#o}", 0)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042)); - EXPECT_EQ("-042", fmt::format("{0:#o}", -042)); - EXPECT_EQ("42", fmt::format("{0:#}", 42u)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42u)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042u)); - - EXPECT_EQ("-42", fmt::format("{0:#}", -42l)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42l)); - EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42l)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042l)); - EXPECT_EQ("-042", fmt::format("{0:#o}", -042l)); - EXPECT_EQ("42", fmt::format("{0:#}", 42ul)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ul)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042ul)); - - EXPECT_EQ("-42", fmt::format("{0:#}", -42ll)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ll)); - EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42ll)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042ll)); - EXPECT_EQ("-042", fmt::format("{0:#o}", -042ll)); - EXPECT_EQ("42", fmt::format("{0:#}", 42ull)); - EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ull)); - EXPECT_EQ("042", fmt::format("{0:#o}", 042ull)); - - EXPECT_EQ("-42.", fmt::format("{0:#}", -42.0)); - EXPECT_EQ("-42.", fmt::format("{0:#}", -42.0l)); - EXPECT_EQ("4.e+01", fmt::format("{:#.0e}", 42.0)); - EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01)); - EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5)); - EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5)); + EXPECT_EQ(fmt::format("{0:#}", 42), "42"); + EXPECT_EQ(fmt::format("{0:#}", -42), "-42"); + EXPECT_EQ(fmt::format("{0:#b}", 42), "0b101010"); + EXPECT_EQ(fmt::format("{0:#B}", 42), "0B101010"); + EXPECT_EQ(fmt::format("{0:#b}", -42), "-0b101010"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42), "0x42"); + EXPECT_EQ(fmt::format("{0:#X}", 0x42), "0X42"); + EXPECT_EQ(fmt::format("{0:#x}", -0x42), "-0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 0), "0"); + EXPECT_EQ(fmt::format("{0:#o}", 042), "042"); + EXPECT_EQ(fmt::format("{0:#o}", -042), "-042"); + EXPECT_EQ(fmt::format("{0:#}", 42u), "42"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42u), "0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 042u), "042"); + + EXPECT_EQ(fmt::format("{0:#}", -42l), "-42"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42l), "0x42"); + EXPECT_EQ(fmt::format("{0:#x}", -0x42l), "-0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 042l), "042"); + EXPECT_EQ(fmt::format("{0:#o}", -042l), "-042"); + EXPECT_EQ(fmt::format("{0:#}", 42ul), "42"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42ul), "0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 042ul), "042"); + + EXPECT_EQ(fmt::format("{0:#}", -42ll), "-42"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42ll), "0x42"); + EXPECT_EQ(fmt::format("{0:#x}", -0x42ll), "-0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 042ll), "042"); + EXPECT_EQ(fmt::format("{0:#o}", -042ll), "-042"); + EXPECT_EQ(fmt::format("{0:#}", 42ull), "42"); + EXPECT_EQ(fmt::format("{0:#x}", 0x42ull), "0x42"); + EXPECT_EQ(fmt::format("{0:#o}", 042ull), "042"); + + EXPECT_EQ(fmt::format("{0:#}", -42.0), "-42."); + EXPECT_EQ(fmt::format("{0:#}", -42.0l), "-42."); + EXPECT_EQ(fmt::format("{:#.0e}", 42.0), "4.e+01"); + EXPECT_EQ(fmt::format("{:#.0f}", 0.01), "0."); + EXPECT_EQ(fmt::format("{:#.2g}", 0.5), "0.50"); + EXPECT_EQ(fmt::format("{:#.0f}", 0.5), "0."); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error, @@ -778,15 +778,15 @@ TEST(format_test, hash_flag) { } TEST(format_test, zero_flag) { - EXPECT_EQ("42", fmt::format("{0:0}", 42)); - EXPECT_EQ("-0042", fmt::format("{0:05}", -42)); - EXPECT_EQ("00042", fmt::format("{0:05}", 42u)); - EXPECT_EQ("-0042", fmt::format("{0:05}", -42l)); - EXPECT_EQ("00042", fmt::format("{0:05}", 42ul)); - EXPECT_EQ("-0042", fmt::format("{0:05}", -42ll)); - EXPECT_EQ("00042", fmt::format("{0:05}", 42ull)); - EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0)); - EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l)); + EXPECT_EQ(fmt::format("{0:0}", 42), "42"); + EXPECT_EQ(fmt::format("{0:05}", -42), "-0042"); + EXPECT_EQ(fmt::format("{0:05}", 42u), "00042"); + EXPECT_EQ(fmt::format("{0:05}", -42l), "-0042"); + EXPECT_EQ(fmt::format("{0:05}", 42ul), "00042"); + EXPECT_EQ(fmt::format("{0:05}", -42ll), "-0042"); + EXPECT_EQ(fmt::format("{0:05}", 42ull), "00042"); + EXPECT_EQ(fmt::format("{0:07}", -42.0), "-000042"); + EXPECT_EQ(fmt::format("{0:07}", -42.0l), "-000042"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error, @@ -801,44 +801,33 @@ TEST(format_test, zero_flag) { TEST(format_test, zero_flag_and_align) { // If the 0 character and an align option both appear, the 0 character is // ignored. - EXPECT_EQ("42 ", fmt::format("{0:<05}", 42)); - EXPECT_EQ("-42 ", fmt::format("{0:<05}", -42)); - EXPECT_EQ(" 42 ", fmt::format("{0:^05}", 42)); - EXPECT_EQ(" -42 ", fmt::format("{0:^05}", -42)); - EXPECT_EQ(" 42", fmt::format("{0:>05}", 42)); - EXPECT_EQ(" -42", fmt::format("{0:>05}", -42)); + EXPECT_EQ(fmt::format("{:<05}", 42), "42 "); + EXPECT_EQ(fmt::format("{:<05}", -42), "-42 "); + EXPECT_EQ(fmt::format("{:^05}", 42), " 42 "); + EXPECT_EQ(fmt::format("{:^05}", -42), " -42 "); + EXPECT_EQ(fmt::format("{:>05}", 42), " 42"); + EXPECT_EQ(fmt::format("{:>05}", -42), " -42"); } TEST(format_test, width) { - char format_str[buffer_size]; - safe_sprintf(format_str, "{0:%u", UINT_MAX); - increment(format_str + 3); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "number is too big"); - size_t size = std::strlen(format_str); - format_str[size] = '}'; - format_str[size + 1] = 0; - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "number is too big"); + auto int_maxer = std::to_string(INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:" + int_maxer), 0), + format_error, "number is too big"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{:" + int_maxer + "}"), 0), + format_error, "number is too big"); - safe_sprintf(format_str, "{0:%u", INT_MAX + 1u); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "number is too big"); - safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "number is too big"); - EXPECT_EQ(" -42", fmt::format("{0:4}", -42)); - EXPECT_EQ(" 42", fmt::format("{0:5}", 42u)); - EXPECT_EQ(" -42", fmt::format("{0:6}", -42l)); - EXPECT_EQ(" 42", fmt::format("{0:7}", 42ul)); - EXPECT_EQ(" -42", fmt::format("{0:6}", -42ll)); - EXPECT_EQ(" 42", fmt::format("{0:7}", 42ull)); - EXPECT_EQ(" -1.23", fmt::format("{0:8}", -1.23)); - EXPECT_EQ(" -1.23", fmt::format("{0:9}", -1.23l)); - EXPECT_EQ(" 0xcafe", - fmt::format("{0:10}", reinterpret_cast(0xcafe))); - EXPECT_EQ("x ", fmt::format("{0:11}", 'x')); - EXPECT_EQ("str ", fmt::format("{0:12}", "str")); + EXPECT_EQ(fmt::format("{:4}", -42), " -42"); + EXPECT_EQ(fmt::format("{:5}", 42u), " 42"); + EXPECT_EQ(fmt::format("{:6}", -42l), " -42"); + EXPECT_EQ(fmt::format("{:7}", 42ul), " 42"); + EXPECT_EQ(fmt::format("{:6}", -42ll), " -42"); + EXPECT_EQ(fmt::format("{:7}", 42ull), " 42"); + EXPECT_EQ(fmt::format("{:8}", -1.23), " -1.23"); + EXPECT_EQ(fmt::format("{:9}", -1.23l), " -1.23"); + EXPECT_EQ(fmt::format("{:10}", reinterpret_cast(0xcafe)), + " 0xcafe"); + EXPECT_EQ(fmt::format("{:11}", 'x'), "x "); + EXPECT_EQ(fmt::format("{:12}", "str"), "str "); EXPECT_EQ(fmt::format("{:*^6}", "🤡"), "**🤡**"); EXPECT_EQ(fmt::format("{:*^8}", "你好"), "**你好**"); EXPECT_EQ(fmt::format("{:#6}", 42.0), " 42."); @@ -847,20 +836,13 @@ TEST(format_test, width) { } TEST(format_test, runtime_width) { - char format_str[buffer_size]; - safe_sprintf(format_str, "{0:{%u", UINT_MAX); - increment(format_str + 4); - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "invalid format string"); - size_t size = std::strlen(format_str); - format_str[size] = '}'; - format_str[size + 1] = 0; - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "argument not found"); - format_str[size + 1] = '}'; - format_str[size + 2] = 0; - EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error, - "argument not found"); + auto int_maxer = std::to_string(INT_MAX + 1u); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer), 0), + format_error, "invalid format string"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer + "}"), 0), + format_error, "argument not found"); + EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer + "}}"), 0), + format_error, "argument not found"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{"), 0), format_error, "invalid format string"); @@ -893,18 +875,18 @@ TEST(format_test, runtime_width) { EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error, "width is not integer"); - EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4)); - EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42u, 5)); - EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42l, 6)); - EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ul, 7)); - EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42ll, 6)); - EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ull, 7)); - EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23, 8)); - EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23l, 9)); + EXPECT_EQ(fmt::format("{0:{1}}", -42, 4), " -42"); + EXPECT_EQ(fmt::format("{0:{1}}", 42u, 5), " 42"); + EXPECT_EQ(fmt::format("{0:{1}}", -42l, 6), " -42"); + EXPECT_EQ(fmt::format("{0:{1}}", 42ul, 7), " 42"); + EXPECT_EQ(fmt::format("{0:{1}}", -42ll, 6), " -42"); + EXPECT_EQ(fmt::format("{0:{1}}", 42ull, 7), " 42"); + EXPECT_EQ(fmt::format("{0:{1}}", -1.23, 8), " -1.23"); + EXPECT_EQ(fmt::format("{0:{1}}", -1.23l, 9), " -1.23"); EXPECT_EQ(" 0xcafe", fmt::format("{0:{1}}", reinterpret_cast(0xcafe), 10)); - EXPECT_EQ("x ", fmt::format("{0:{1}}", 'x', 11)); - EXPECT_EQ("str ", fmt::format("{0:{1}}", "str", 12)); + EXPECT_EQ(fmt::format("{0:{1}}", 'x', 11), "x "); + EXPECT_EQ(fmt::format("{0:{1}}", "str", 12), "str "); EXPECT_EQ(fmt::format("{:{}}", 42, short(4)), " 42"); } @@ -960,12 +942,12 @@ TEST(format_test, precision) { "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.0}"), 'x'), format_error, "invalid format specifier"); - EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345)); - EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l)); - EXPECT_EQ("1.2e+56", fmt::format("{:.2}", 1.234e56)); - EXPECT_EQ("1.1", fmt::format("{0:.3}", 1.1)); - EXPECT_EQ("1e+00", fmt::format("{:.0e}", 1.0L)); - EXPECT_EQ(" 0.0e+00", fmt::format("{:9.1e}", 0.0)); + EXPECT_EQ(fmt::format("{0:.2}", 1.2345), "1.2"); + EXPECT_EQ(fmt::format("{0:.2}", 1.2345l), "1.2"); + EXPECT_EQ(fmt::format("{:.2}", 1.234e56), "1.2e+56"); + EXPECT_EQ(fmt::format("{0:.3}", 1.1), "1.1"); + EXPECT_EQ(fmt::format("{:.0e}", 1.0L), "1e+00"); + EXPECT_EQ(fmt::format("{:9.1e}", 0.0), " 0.0e+00"); EXPECT_EQ( fmt::format("{:.494}", 4.9406564584124654E-324), "4.9406564584124654417656879286822137236505980261432476442558568250067550" @@ -1029,12 +1011,12 @@ TEST(format_test, precision) { "3.6452e-4951"); } - EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); - EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); - EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); - EXPECT_EQ("1019666400", fmt::format("{}", 1019666432.0f)); - EXPECT_EQ("1e+01", fmt::format("{:.0e}", 9.5)); - EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34)); + EXPECT_EQ(fmt::format("{:#.0f}", 123.0), "123."); + EXPECT_EQ(fmt::format("{:.02f}", 1.234), "1.23"); + EXPECT_EQ(fmt::format("{:.1g}", 0.001), "0.001"); + EXPECT_EQ(fmt::format("{}", 1019666432.0f), "1019666400"); + EXPECT_EQ(fmt::format("{:.0e}", 9.5), "1e+01"); + EXPECT_EQ(fmt::format("{:.1e}", 1e-34), "1.0e-34"); EXPECT_THROW_MSG( (void)fmt::format(runtime("{0:.2}"), reinterpret_cast(0xcafe)), @@ -1049,9 +1031,9 @@ TEST(format_test, precision) { (void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304), format_error, "number is too big"); - EXPECT_EQ("st", fmt::format("{0:.2}", "str")); - EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі")); - EXPECT_EQ("123456", fmt::format("{0:.6}", "123456\xad")); + EXPECT_EQ(fmt::format("{0:.2}", "str"), "st"); + EXPECT_EQ(fmt::format("{0:.5}", "вожыкі"), "вожык"); + EXPECT_EQ(fmt::format("{0:.6}", "123456\xad"), "123456"); } TEST(xchar_test, utf8_precision) { @@ -1135,8 +1117,8 @@ TEST(format_test, runtime_precision) { format_error, "invalid format specifier"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.{1}}"), 'x', 0), format_error, "invalid format specifier"); - EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2)); - EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l)); + EXPECT_EQ(fmt::format("{0:.{1}}", 1.2345, 2), "1.2"); + EXPECT_EQ(fmt::format("{1:.{0}}", 2, 1.2345l), "1.2"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), reinterpret_cast(0xcafe), 2), @@ -1145,26 +1127,26 @@ TEST(format_test, runtime_precision) { reinterpret_cast(0xcafe), 2), format_error, "invalid format specifier"); - EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2)); + EXPECT_EQ(fmt::format("{0:.{1}}", "str", 2), "st"); } TEST(format_test, format_bool) { - EXPECT_EQ("true", fmt::format("{}", true)); - EXPECT_EQ("false", fmt::format("{}", false)); - EXPECT_EQ("1", fmt::format("{:d}", true)); - EXPECT_EQ("true ", fmt::format("{:5}", true)); - EXPECT_EQ("true", fmt::format("{:s}", true)); - EXPECT_EQ("false", fmt::format("{:s}", false)); - EXPECT_EQ("false ", fmt::format("{:6s}", false)); + EXPECT_EQ(fmt::format("{}", true), "true"); + EXPECT_EQ(fmt::format("{}", false), "false"); + EXPECT_EQ(fmt::format("{:d}", true), "1"); + EXPECT_EQ(fmt::format("{:5}", true), "true "); + EXPECT_EQ(fmt::format("{:s}", true), "true"); + EXPECT_EQ(fmt::format("{:s}", false), "false"); + EXPECT_EQ(fmt::format("{:6s}", false), "false "); EXPECT_THROW_MSG((void)fmt::format(runtime("{:c}"), false), format_error, "invalid format specifier"); } TEST(format_test, format_short) { short s = 42; - EXPECT_EQ("42", fmt::format("{0:d}", s)); + EXPECT_EQ(fmt::format("{0:d}", s), "42"); unsigned short us = 42; - EXPECT_EQ("42", fmt::format("{0:d}", us)); + EXPECT_EQ(fmt::format("{0:d}", us), "42"); } template @@ -1186,16 +1168,16 @@ TEST(format_test, format_int) { EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error, "invalid format specifier"); check_unknown_types(42, "bBdoxXnLc", "integer"); - EXPECT_EQ("x", fmt::format("{:c}", static_cast('x'))); + EXPECT_EQ(fmt::format("{:c}", static_cast('x')), "x"); } TEST(format_test, format_bin) { - EXPECT_EQ("0", fmt::format("{0:b}", 0)); - EXPECT_EQ("101010", fmt::format("{0:b}", 42)); - EXPECT_EQ("101010", fmt::format("{0:b}", 42u)); - EXPECT_EQ("-101010", fmt::format("{0:b}", -42)); - EXPECT_EQ("11000000111001", fmt::format("{0:b}", 12345)); - EXPECT_EQ("10010001101000101011001111000", fmt::format("{0:b}", 0x12345678)); + EXPECT_EQ(fmt::format("{0:b}", 0), "0"); + EXPECT_EQ(fmt::format("{0:b}", 42), "101010"); + EXPECT_EQ(fmt::format("{0:b}", 42u), "101010"); + EXPECT_EQ(fmt::format("{0:b}", -42), "-101010"); + EXPECT_EQ(fmt::format("{0:b}", 12345), "11000000111001"); + EXPECT_EQ(fmt::format("{0:b}", 0x12345678), "10010001101000101011001111000"); EXPECT_EQ("10010000101010111100110111101111", fmt::format("{0:b}", 0x90ABCDEF)); EXPECT_EQ("11111111111111111111111111111111", @@ -1211,17 +1193,17 @@ constexpr auto uint128_max = ~static_cast<__uint128_t>(0); #endif TEST(format_test, format_dec) { - EXPECT_EQ("0", fmt::format("{0}", 0)); - EXPECT_EQ("42", fmt::format("{0}", 42)); - EXPECT_EQ("42>", fmt::format("{:}>", 42)); - EXPECT_EQ("42", fmt::format("{0:d}", 42)); - EXPECT_EQ("42", fmt::format("{0}", 42u)); - EXPECT_EQ("-42", fmt::format("{0}", -42)); - EXPECT_EQ("12345", fmt::format("{0}", 12345)); - EXPECT_EQ("67890", fmt::format("{0}", 67890)); + EXPECT_EQ(fmt::format("{0}", 0), "0"); + EXPECT_EQ(fmt::format("{0}", 42), "42"); + EXPECT_EQ(fmt::format("{:}>", 42), "42>"); + EXPECT_EQ(fmt::format("{0:d}", 42), "42"); + EXPECT_EQ(fmt::format("{0}", 42u), "42"); + EXPECT_EQ(fmt::format("{0}", -42), "-42"); + EXPECT_EQ(fmt::format("{0}", 12345), "12345"); + EXPECT_EQ(fmt::format("{0}", 67890), "67890"); #if FMT_USE_INT128 - EXPECT_EQ("0", fmt::format("{0}", static_cast<__int128_t>(0))); - EXPECT_EQ("0", fmt::format("{0}", static_cast<__uint128_t>(0))); + EXPECT_EQ(fmt::format("{0}", static_cast<__int128_t>(0)), "0"); + EXPECT_EQ(fmt::format("{0}", static_cast<__uint128_t>(0)), "0"); EXPECT_EQ("9223372036854775808", fmt::format("{0}", static_cast<__int128_t>(INT64_MAX) + 1)); EXPECT_EQ("-9223372036854775809", @@ -1252,17 +1234,17 @@ TEST(format_test, format_dec) { } TEST(format_test, format_hex) { - EXPECT_EQ("0", fmt::format("{0:x}", 0)); - EXPECT_EQ("42", fmt::format("{0:x}", 0x42)); - EXPECT_EQ("42", fmt::format("{0:x}", 0x42u)); - EXPECT_EQ("-42", fmt::format("{0:x}", -0x42)); - EXPECT_EQ("12345678", fmt::format("{0:x}", 0x12345678)); - EXPECT_EQ("90abcdef", fmt::format("{0:x}", 0x90abcdef)); - EXPECT_EQ("12345678", fmt::format("{0:X}", 0x12345678)); - EXPECT_EQ("90ABCDEF", fmt::format("{0:X}", 0x90ABCDEF)); + EXPECT_EQ(fmt::format("{0:x}", 0), "0"); + EXPECT_EQ(fmt::format("{0:x}", 0x42), "42"); + EXPECT_EQ(fmt::format("{0:x}", 0x42u), "42"); + EXPECT_EQ(fmt::format("{0:x}", -0x42), "-42"); + EXPECT_EQ(fmt::format("{0:x}", 0x12345678), "12345678"); + EXPECT_EQ(fmt::format("{0:x}", 0x90abcdef), "90abcdef"); + EXPECT_EQ(fmt::format("{0:X}", 0x12345678), "12345678"); + EXPECT_EQ(fmt::format("{0:X}", 0x90ABCDEF), "90ABCDEF"); #if FMT_USE_INT128 - EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__int128_t>(0))); - EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__uint128_t>(0))); + EXPECT_EQ(fmt::format("{0:x}", static_cast<__int128_t>(0)), "0"); + EXPECT_EQ(fmt::format("{0:x}", static_cast<__uint128_t>(0)), "0"); EXPECT_EQ("8000000000000000", fmt::format("{0:x}", static_cast<__int128_t>(INT64_MAX) + 1)); EXPECT_EQ("-8000000000000001", @@ -1293,14 +1275,14 @@ TEST(format_test, format_hex) { } TEST(format_test, format_oct) { - EXPECT_EQ("0", fmt::format("{0:o}", 0)); - EXPECT_EQ("42", fmt::format("{0:o}", 042)); - EXPECT_EQ("42", fmt::format("{0:o}", 042u)); - EXPECT_EQ("-42", fmt::format("{0:o}", -042)); - EXPECT_EQ("12345670", fmt::format("{0:o}", 012345670)); + EXPECT_EQ(fmt::format("{0:o}", 0), "0"); + EXPECT_EQ(fmt::format("{0:o}", 042), "42"); + EXPECT_EQ(fmt::format("{0:o}", 042u), "42"); + EXPECT_EQ(fmt::format("{0:o}", -042), "-42"); + EXPECT_EQ(fmt::format("{0:o}", 012345670), "12345670"); #if FMT_USE_INT128 - EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__int128_t>(0))); - EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__uint128_t>(0))); + EXPECT_EQ(fmt::format("{0:o}", static_cast<__int128_t>(0)), "0"); + EXPECT_EQ(fmt::format("{0:o}", static_cast<__uint128_t>(0)), "0"); EXPECT_EQ("1000000000000000000000", fmt::format("{0:o}", static_cast<__int128_t>(INT64_MAX) + 1)); EXPECT_EQ("-1000000000000000000001", @@ -1331,12 +1313,12 @@ TEST(format_test, format_oct) { } TEST(format_test, format_int_locale) { - EXPECT_EQ("1234", fmt::format("{:L}", 1234)); + EXPECT_EQ(fmt::format("{:L}", 1234), "1234"); } TEST(format_test, format_float) { - EXPECT_EQ("0", fmt::format("{}", 0.0f)); - EXPECT_EQ("392.500000", fmt::format("{0:f}", 392.5f)); + EXPECT_EQ(fmt::format("{}", 0.0f), "0"); + EXPECT_EQ(fmt::format("{0:f}", 392.5f), "392.500000"); } TEST(format_test, format_double) { @@ -1405,92 +1387,92 @@ TEST(format_test, format_double) { } TEST(format_test, precision_rounding) { - EXPECT_EQ("0", fmt::format("{:.0f}", 0.0)); - EXPECT_EQ("0", fmt::format("{:.0f}", 0.01)); - EXPECT_EQ("0", fmt::format("{:.0f}", 0.1)); - EXPECT_EQ("0.000", fmt::format("{:.3f}", 0.00049)); - EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.0005)); - EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.00149)); - EXPECT_EQ("0.002", fmt::format("{:.3f}", 0.0015)); - EXPECT_EQ("1.000", fmt::format("{:.3f}", 0.9999)); - EXPECT_EQ("0.00123", fmt::format("{:.3}", 0.00123)); - EXPECT_EQ("0.1", fmt::format("{:.16g}", 0.1)); - EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); + EXPECT_EQ(fmt::format("{:.0f}", 0.0), "0"); + EXPECT_EQ(fmt::format("{:.0f}", 0.01), "0"); + EXPECT_EQ(fmt::format("{:.0f}", 0.1), "0"); + EXPECT_EQ(fmt::format("{:.3f}", 0.00049), "0.000"); + EXPECT_EQ(fmt::format("{:.3f}", 0.0005), "0.001"); + EXPECT_EQ(fmt::format("{:.3f}", 0.00149), "0.001"); + EXPECT_EQ(fmt::format("{:.3f}", 0.0015), "0.002"); + EXPECT_EQ(fmt::format("{:.3f}", 0.9999), "1.000"); + EXPECT_EQ(fmt::format("{:.3}", 0.00123), "0.00123"); + EXPECT_EQ(fmt::format("{:.16g}", 0.1), "0.1"); + EXPECT_EQ(fmt::format("{:.0}", 1.0), "1"); EXPECT_EQ("225.51575035152063720", fmt::format("{:.17f}", 225.51575035152064)); - EXPECT_EQ("-761519619559038.2", fmt::format("{:.1f}", -761519619559038.2)); + EXPECT_EQ(fmt::format("{:.1f}", -761519619559038.2), "-761519619559038.2"); EXPECT_EQ("1.9156918820264798e-56", fmt::format("{}", 1.9156918820264798e-56)); - EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15)); + EXPECT_EQ(fmt::format("{:.4f}", 7.2809479766055470e-15), "0.0000"); } TEST(format_test, prettify_float) { - EXPECT_EQ("0.0001", fmt::format("{}", 1e-4)); - EXPECT_EQ("1e-05", fmt::format("{}", 1e-5)); - EXPECT_EQ("1000000000000000", fmt::format("{}", 1e15)); - EXPECT_EQ("1e+16", fmt::format("{}", 1e16)); - EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5)); - EXPECT_EQ("10000000000", fmt::format("{}", 1e10)); - EXPECT_EQ("100000000000", fmt::format("{}", 1e11)); - EXPECT_EQ("12340000000", fmt::format("{}", 1234e7)); - EXPECT_EQ("12.34", fmt::format("{}", 1234e-2)); - EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6)); - EXPECT_EQ("0.1", fmt::format("{}", 0.1f)); - EXPECT_EQ("1.3563156e-19", fmt::format("{}", 1.35631564e-19f)); + EXPECT_EQ(fmt::format("{}", 1e-4), "0.0001"); + EXPECT_EQ(fmt::format("{}", 1e-5), "1e-05"); + EXPECT_EQ(fmt::format("{}", 1e15), "1000000000000000"); + EXPECT_EQ(fmt::format("{}", 1e16), "1e+16"); + EXPECT_EQ(fmt::format("{}", 9.999e-5), "9.999e-05"); + EXPECT_EQ(fmt::format("{}", 1e10), "10000000000"); + EXPECT_EQ(fmt::format("{}", 1e11), "100000000000"); + EXPECT_EQ(fmt::format("{}", 1234e7), "12340000000"); + EXPECT_EQ(fmt::format("{}", 1234e-2), "12.34"); + EXPECT_EQ(fmt::format("{}", 1234e-6), "0.001234"); + EXPECT_EQ(fmt::format("{}", 0.1f), "0.1"); + EXPECT_EQ(fmt::format("{}", 1.35631564e-19f), "1.3563156e-19"); } TEST(format_test, format_nan) { double nan = std::numeric_limits::quiet_NaN(); - EXPECT_EQ("nan", fmt::format("{}", nan)); - EXPECT_EQ("+nan", fmt::format("{:+}", nan)); - EXPECT_EQ(" +nan", fmt::format("{:+06}", nan)); - EXPECT_EQ("+nan ", fmt::format("{:<+06}", nan)); - EXPECT_EQ(" +nan ", fmt::format("{:^+06}", nan)); - EXPECT_EQ(" +nan", fmt::format("{:>+06}", nan)); + EXPECT_EQ(fmt::format("{}", nan), "nan"); + EXPECT_EQ(fmt::format("{:+}", nan), "+nan"); + EXPECT_EQ(fmt::format("{:+06}", nan), " +nan"); + EXPECT_EQ(fmt::format("{:<+06}", nan), "+nan "); + EXPECT_EQ(fmt::format("{:^+06}", nan), " +nan "); + EXPECT_EQ(fmt::format("{:>+06}", nan), " +nan"); if (std::signbit(-nan)) { - EXPECT_EQ("-nan", fmt::format("{}", -nan)); - EXPECT_EQ(" -nan", fmt::format("{:+06}", -nan)); + EXPECT_EQ(fmt::format("{}", -nan), "-nan"); + EXPECT_EQ(fmt::format("{:+06}", -nan), " -nan"); } else { fmt::print("Warning: compiler doesn't handle negative NaN correctly"); } - EXPECT_EQ(" nan", fmt::format("{: }", nan)); - EXPECT_EQ("NAN", fmt::format("{:F}", nan)); - EXPECT_EQ("nan ", fmt::format("{:<7}", nan)); - EXPECT_EQ(" nan ", fmt::format("{:^7}", nan)); - EXPECT_EQ(" nan", fmt::format("{:>7}", nan)); + EXPECT_EQ(fmt::format("{: }", nan), " nan"); + EXPECT_EQ(fmt::format("{:F}", nan), "NAN"); + EXPECT_EQ(fmt::format("{:<7}", nan), "nan "); + EXPECT_EQ(fmt::format("{:^7}", nan), " nan "); + EXPECT_EQ(fmt::format("{:>7}", nan), " nan"); } TEST(format_test, format_infinity) { double inf = std::numeric_limits::infinity(); - EXPECT_EQ("inf", fmt::format("{}", inf)); - EXPECT_EQ("+inf", fmt::format("{:+}", inf)); - EXPECT_EQ("-inf", fmt::format("{}", -inf)); - EXPECT_EQ(" +inf", fmt::format("{:+06}", inf)); - EXPECT_EQ(" -inf", fmt::format("{:+06}", -inf)); - EXPECT_EQ("+inf ", fmt::format("{:<+06}", inf)); - EXPECT_EQ(" +inf ", fmt::format("{:^+06}", inf)); - EXPECT_EQ(" +inf", fmt::format("{:>+06}", inf)); - EXPECT_EQ(" inf", fmt::format("{: }", inf)); - EXPECT_EQ("INF", fmt::format("{:F}", inf)); - EXPECT_EQ("inf ", fmt::format("{:<7}", inf)); - EXPECT_EQ(" inf ", fmt::format("{:^7}", inf)); - EXPECT_EQ(" inf", fmt::format("{:>7}", inf)); + EXPECT_EQ(fmt::format("{}", inf), "inf"); + EXPECT_EQ(fmt::format("{:+}", inf), "+inf"); + EXPECT_EQ(fmt::format("{}", -inf), "-inf"); + EXPECT_EQ(fmt::format("{:+06}", inf), " +inf"); + EXPECT_EQ(fmt::format("{:+06}", -inf), " -inf"); + EXPECT_EQ(fmt::format("{:<+06}", inf), "+inf "); + EXPECT_EQ(fmt::format("{:^+06}", inf), " +inf "); + EXPECT_EQ(fmt::format("{:>+06}", inf), " +inf"); + EXPECT_EQ(fmt::format("{: }", inf), " inf"); + EXPECT_EQ(fmt::format("{:F}", inf), "INF"); + EXPECT_EQ(fmt::format("{:<7}", inf), "inf "); + EXPECT_EQ(fmt::format("{:^7}", inf), " inf "); + EXPECT_EQ(fmt::format("{:>7}", inf), " inf"); } TEST(format_test, format_long_double) { - EXPECT_EQ("0", fmt::format("{0:}", 0.0l)); - EXPECT_EQ("0.000000", fmt::format("{0:f}", 0.0l)); - EXPECT_EQ("0.0", fmt::format("{:.1f}", 0.000000001l)); - EXPECT_EQ("0.10", fmt::format("{:.2f}", 0.099l)); - EXPECT_EQ("392.65", fmt::format("{0:}", 392.65l)); - EXPECT_EQ("392.65", fmt::format("{0:g}", 392.65l)); - EXPECT_EQ("392.65", fmt::format("{0:G}", 392.65l)); - EXPECT_EQ("392.650000", fmt::format("{0:f}", 392.65l)); - EXPECT_EQ("392.650000", fmt::format("{0:F}", 392.65l)); + EXPECT_EQ(fmt::format("{0:}", 0.0l), "0"); + EXPECT_EQ(fmt::format("{0:f}", 0.0l), "0.000000"); + EXPECT_EQ(fmt::format("{:.1f}", 0.000000001l), "0.0"); + EXPECT_EQ(fmt::format("{:.2f}", 0.099l), "0.10"); + EXPECT_EQ(fmt::format("{0:}", 392.65l), "392.65"); + EXPECT_EQ(fmt::format("{0:g}", 392.65l), "392.65"); + EXPECT_EQ(fmt::format("{0:G}", 392.65l), "392.65"); + EXPECT_EQ(fmt::format("{0:f}", 392.65l), "392.650000"); + EXPECT_EQ(fmt::format("{0:F}", 392.65l), "392.650000"); char buffer[buffer_size]; safe_sprintf(buffer, "%Le", 392.65l); EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65l)); - EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.64l)); + EXPECT_EQ(fmt::format("{0:+010.4g}", 392.64l), "+0000392.6"); auto ld = 3.31l; if (fmt::detail::is_double_double::value) { @@ -1504,8 +1486,8 @@ TEST(format_test, format_long_double) { TEST(format_test, format_char) { const char types[] = "cbBdoxX"; check_unknown_types('a', types, "char"); - EXPECT_EQ("a", fmt::format("{0}", 'a')); - EXPECT_EQ("z", fmt::format("{0:c}", 'z')); + EXPECT_EQ(fmt::format("{0}", 'a'), "a"); + EXPECT_EQ(fmt::format("{0:c}", 'z'), "z"); int n = 'x'; for (const char* type = types + 1; *type; ++type) { std::string format_str = fmt::format("{{:{}}}", *type); @@ -1515,27 +1497,27 @@ TEST(format_test, format_char) { } EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); - EXPECT_EQ("\n", fmt::format("{}", '\n')); - EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n')); - EXPECT_EQ("ff", fmt::format("{:x}", '\xff')); + EXPECT_EQ(fmt::format("{}", '\n'), "\n"); + EXPECT_EQ(fmt::format("{:?}", '\n'), "'\\n'"); + EXPECT_EQ(fmt::format("{:x}", '\xff'), "ff"); } TEST(format_test, format_volatile_char) { volatile char c = 'x'; - EXPECT_EQ("x", fmt::format("{}", c)); + EXPECT_EQ(fmt::format("{}", c), "x"); } TEST(format_test, format_unsigned_char) { - EXPECT_EQ("42", fmt::format("{}", static_cast(42))); - EXPECT_EQ("42", fmt::format("{}", static_cast(42))); + EXPECT_EQ(fmt::format("{}", static_cast(42)), "42"); + EXPECT_EQ(fmt::format("{}", static_cast(42)), "42"); } TEST(format_test, format_cstring) { check_unknown_types("test", "sp", "string"); - EXPECT_EQ("test", fmt::format("{0}", "test")); - EXPECT_EQ("test", fmt::format("{0:s}", "test")); + EXPECT_EQ(fmt::format("{0}", "test"), "test"); + EXPECT_EQ(fmt::format("{0:s}", "test"), "test"); char nonconst[] = "nonconst"; - EXPECT_EQ("nonconst", fmt::format("{0}", nonconst)); + EXPECT_EQ(fmt::format("{0}", nonconst), "nonconst"); auto nullstr = static_cast(nullptr); EXPECT_THROW_MSG((void)fmt::format("{}", nullstr), format_error, "string pointer is null"); @@ -1547,9 +1529,9 @@ void function_pointer_test(int, double, std::string) {} TEST(format_test, format_pointer) { check_unknown_types(reinterpret_cast(0x1234), "p", "pointer"); - EXPECT_EQ("0x0", fmt::format("{0}", static_cast(nullptr))); - EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast(0x1234))); - EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast(0x1234))); + EXPECT_EQ(fmt::format("{0}", static_cast(nullptr)), "0x0"); + EXPECT_EQ(fmt::format("{0}", reinterpret_cast(0x1234)), "0x1234"); + EXPECT_EQ(fmt::format("{0:p}", reinterpret_cast(0x1234)), "0x1234"); // On CHERI (or other fat-pointer) systems, the size of a pointer is greater // than the size an integer that can hold a virtual address. There is no // portable address-as-an-integer type (yet) in C++, so we use `size_t` as @@ -1573,7 +1555,7 @@ TEST(format_test, format_pointer) { EXPECT_EQ(fmt::format("{}", fmt::detail::bit_cast( &function_pointer_test)), fmt::format("{}", fmt::ptr(function_pointer_test))); - EXPECT_EQ("0x0", fmt::format("{}", nullptr)); + EXPECT_EQ(fmt::format("{}", nullptr), "0x0"); } TEST(format_test, write_uintptr_fallback) { @@ -1611,9 +1593,9 @@ TEST(format_test, format_string) { } TEST(format_test, format_string_view) { - EXPECT_EQ("test", fmt::format("{}", string_view("test"))); - EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst"))); - EXPECT_EQ("", fmt::format("{}", string_view())); + EXPECT_EQ(fmt::format("{}", string_view("test")), "test"); + EXPECT_EQ(fmt::format("{:?}", string_view("t\nst")), "\"t\\nst\""); + EXPECT_EQ(fmt::format("{}", string_view()), ""); } #ifdef FMT_USE_STRING_VIEW @@ -1628,8 +1610,8 @@ template <> struct formatter : formatter { FMT_END_NAMESPACE TEST(format_test, format_std_string_view) { - EXPECT_EQ("test", fmt::format("{}", std::string_view("test"))); - EXPECT_EQ("foo", fmt::format("{}", string_viewable())); + EXPECT_EQ(fmt::format("{}", std::string_view("test")), "test"); + EXPECT_EQ(fmt::format("{}", string_viewable()), "foo"); } struct explicitly_convertible_to_std_string_view { @@ -1680,8 +1662,8 @@ FMT_END_NAMESPACE TEST(format_test, format_custom) { EXPECT_THROW_MSG((void)fmt::format(runtime("{:s}"), date(2012, 12, 9)), format_error, "unknown format specifier"); - EXPECT_EQ("42", fmt::format("{0}", Answer())); - EXPECT_EQ("0042", fmt::format("{:04}", Answer())); + EXPECT_EQ(fmt::format("{0}", Answer()), "42"); + EXPECT_EQ(fmt::format("{:04}", Answer()), "0042"); } TEST(format_test, format_to_custom) { @@ -1701,7 +1683,7 @@ TEST(format_test, format_examples) { std::string message = fmt::format("The answer is {}", 42); EXPECT_EQ("The answer is 42", message); - EXPECT_EQ("42", fmt::format("{}", 42)); + EXPECT_EQ(fmt::format("{}", 42), "42"); memory_buffer out; fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); @@ -1723,17 +1705,17 @@ TEST(format_test, format_examples) { EXPECT_EQ("First, thou shalt count to three", fmt::format("First, thou shalt count to {0}", "three")); - EXPECT_EQ("Bring me a shrubbery", fmt::format("Bring me a {}", "shrubbery")); - EXPECT_EQ("From 1 to 3", fmt::format("From {} to {}", 1, 3)); + EXPECT_EQ(fmt::format("Bring me a {}", "shrubbery"), "Bring me a shrubbery"); + EXPECT_EQ(fmt::format("From {} to {}", 1, 3), "From 1 to 3"); char buffer[buffer_size]; safe_sprintf(buffer, "%03.2f", -1.2); EXPECT_EQ(buffer, fmt::format("{:03.2f}", -1.2)); - EXPECT_EQ("a, b, c", fmt::format("{0}, {1}, {2}", 'a', 'b', 'c')); - EXPECT_EQ("a, b, c", fmt::format("{}, {}, {}", 'a', 'b', 'c')); - EXPECT_EQ("c, b, a", fmt::format("{2}, {1}, {0}", 'a', 'b', 'c')); - EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad")); + EXPECT_EQ(fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'), "a, b, c"); + EXPECT_EQ(fmt::format("{}, {}, {}", 'a', 'b', 'c'), "a, b, c"); + EXPECT_EQ(fmt::format("{2}, {1}, {0}", 'a', 'b', 'c'), "c, b, a"); + EXPECT_EQ(fmt::format("{0}{1}{0}", "abra", "cad"), "abracadabra"); EXPECT_EQ("left aligned ", fmt::format("{:<30}", "left aligned")); @@ -1744,16 +1726,16 @@ TEST(format_test, format_examples) { EXPECT_EQ("***********centered***********", fmt::format("{:*^30}", "centered")); - EXPECT_EQ("+3.140000; -3.140000", fmt::format("{:+f}; {:+f}", 3.14, -3.14)); - EXPECT_EQ(" 3.140000; -3.140000", fmt::format("{: f}; {: f}", 3.14, -3.14)); - EXPECT_EQ("3.140000; -3.140000", fmt::format("{:-f}; {:-f}", 3.14, -3.14)); + EXPECT_EQ(fmt::format("{:+f}; {:+f}", 3.14, -3.14), "+3.140000; -3.140000"); + EXPECT_EQ(fmt::format("{: f}; {: f}", 3.14, -3.14), " 3.140000; -3.140000"); + EXPECT_EQ(fmt::format("{:-f}; {:-f}", 3.14, -3.14), "3.140000; -3.140000"); EXPECT_EQ("int: 42; hex: 2a; oct: 52", fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}", 42)); EXPECT_EQ("int: 42; hex: 0x2a; oct: 052", fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42)); - EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42)); + EXPECT_EQ(fmt::format("The answer is {}", 42), "The answer is 42"); EXPECT_THROW_MSG( (void)fmt::format(runtime("The answer is {:d}"), "forty-two"), format_error, "invalid format specifier"); @@ -1772,7 +1754,7 @@ TEST(format_test, print) { } TEST(format_test, variadic) { - EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1)); + EXPECT_EQ(fmt::format("{}c{}", "ab", 1), "abc1"); } TEST(format_test, bytes) { @@ -1817,23 +1799,23 @@ TEST(format_test, join) { v2.push_back(3.4f); void* v3[2] = {&v1[0], &v1[1]}; - EXPECT_EQ("(1, 2, 3)", fmt::format("({})", join(v1, v1 + 3, ", "))); - EXPECT_EQ("(1)", fmt::format("({})", join(v1, v1 + 1, ", "))); - EXPECT_EQ("()", fmt::format("({})", join(v1, v1, ", "))); - EXPECT_EQ("(001, 002, 003)", fmt::format("({:03})", join(v1, v1 + 3, ", "))); + 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("1, 2, 3", fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1)); + 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("(1, 2, 3)", fmt::format("({})", join(v1, ", "))); - EXPECT_EQ("(+01.20, +03.40)", fmt::format("({:+06.2f})", join(v2, ", "))); + 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("0 1 0", fmt::format("{}", join(v4, " "))); + EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0"); } #ifdef __cpp_lib_byte @@ -1880,45 +1862,45 @@ static constexpr const char static_with_null[3] = {'{', '}', '\0'}; static constexpr const char static_no_null[2] = {'{', '}'}; TEST(format_test, compile_time_string) { - EXPECT_EQ("foo", fmt::format(FMT_STRING("foo"))); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); + EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo"); + EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42"); #if FMT_USE_NONTYPE_TEMPLATE_ARGS using namespace fmt::literals; EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar", "foo"_a = "foo")); - EXPECT_EQ("", fmt::format(FMT_STRING(""))); - EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer())); - EXPECT_EQ("1 2", fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2)); + EXPECT_EQ(fmt::format(FMT_STRING("")), ""); + EXPECT_EQ(fmt::format(FMT_STRING(""), "arg"_a = 42), ""); + EXPECT_EQ(fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()), "42"); + EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2"); #endif (void)static_with_null; (void)static_no_null; #ifndef _MSC_VER - EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42)); + EXPECT_EQ(fmt::format(FMT_STRING(static_with_null), 42), "42"); + EXPECT_EQ(fmt::format(FMT_STRING(static_no_null), 42), "42"); #endif (void)with_null; (void)no_null; #if FMT_CPLUSPLUS >= 201703L - EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); + EXPECT_EQ(fmt::format(FMT_STRING(with_null), 42), "42"); + EXPECT_EQ(fmt::format(FMT_STRING(no_null), 42), "42"); #endif #if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L - EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); + EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42"); #endif } TEST(format_test, custom_format_compile_time_string) { - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer())); + EXPECT_EQ(fmt::format(FMT_STRING("{}"), Answer()), "42"); auto answer = Answer(); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer)); + EXPECT_EQ(fmt::format(FMT_STRING("{}"), answer), "42"); char buf[10] = {}; fmt::format_to(buf, FMT_STRING("{}"), answer); const Answer const_answer = Answer(); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); + EXPECT_EQ(fmt::format(FMT_STRING("{}"), const_answer), "42"); } #if FMT_USE_USER_DEFINED_LITERALS @@ -1931,11 +1913,11 @@ TEST(format_test, named_arg_udl) { fmt::arg("second", "cad"), fmt::arg("third", 99)), udl_a); - EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer())); + EXPECT_EQ(fmt::format("{answer}", "answer"_a = Answer()), "42"); } #endif // FMT_USE_USER_DEFINED_LITERALS -TEST(format_test, enum) { EXPECT_EQ("0", fmt::format("{}", foo)); } +TEST(format_test, enum) { EXPECT_EQ(fmt::format("{}", foo), "0"); } TEST(format_test, formatter_not_specialized) { static_assert(!fmt::has_formatter, @@ -1948,12 +1930,12 @@ enum big_enum : unsigned long long { big_enum_value = 5000000000ULL }; auto format_as(big_enum e) -> unsigned long long { return e; } TEST(format_test, strong_enum) { - EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value)); + EXPECT_EQ(fmt::format("{}", big_enum_value), "5000000000"); } #endif TEST(format_test, non_null_terminated_format_string) { - EXPECT_EQ("42", fmt::format(string_view("{}foo", 2), 42)); + EXPECT_EQ(fmt::format(string_view("{}foo", 2), 42), "42"); } namespace adl_test { @@ -2086,16 +2068,16 @@ struct test_output_iterator { using pointer = void; using reference = void; - test_output_iterator& operator++() { + auto operator++() -> test_output_iterator& { ++data; return *this; } - test_output_iterator operator++(int) { + auto operator++(int) -> test_output_iterator { auto tmp = *this; ++data; return tmp; } - char& operator*() { return *data; } + auto operator*() -> char& { return *data; } }; TEST(format_test, format_to_n_output_iterator) { @@ -2110,13 +2092,13 @@ TEST(format_test, vformat_to) { auto args = fmt::make_format_args(n); auto s = std::string(); fmt::vformat_to(std::back_inserter(s), "{}", args); - EXPECT_EQ("42", s); + EXPECT_EQ(s, "42"); s.clear(); fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); - EXPECT_EQ("42", s); + EXPECT_EQ(s, "42"); } -TEST(format_test, char_traits_is_not_ambiguous) { +TEST(format_test, char_traits_not_ambiguous) { // Test that we don't inject detail names into the std namespace. using namespace std; auto c = char_traits::char_type(); @@ -2187,7 +2169,7 @@ TEST(format_test, format_as_to_string) { EXPECT_EQ(fmt::to_string(test::struct_as_int()), "42"); } -template bool check_enabled_formatter() { +template auto check_enabled_formatter() -> bool { static_assert(std::is_default_constructible>::value, ""); return true; @@ -2213,21 +2195,20 @@ TEST(format_test, test_formatters_enabled) { TEST(format_int_test, data) { fmt::format_int format_int(42); - EXPECT_EQ("42", std::string(format_int.data(), format_int.size())); + EXPECT_EQ(std::string(format_int.data(), format_int.size()), "42"); } TEST(format_int_test, format_int) { - EXPECT_EQ("42", fmt::format_int(42).str()); - EXPECT_EQ(2u, fmt::format_int(42).size()); - EXPECT_EQ("-42", fmt::format_int(-42).str()); - EXPECT_EQ(3u, fmt::format_int(-42).size()); - EXPECT_EQ("42", fmt::format_int(42ul).str()); - EXPECT_EQ("-42", fmt::format_int(-42l).str()); - EXPECT_EQ("42", fmt::format_int(42ull).str()); - EXPECT_EQ("-42", fmt::format_int(-42ll).str()); - std::ostringstream os; - os << max_value(); - EXPECT_EQ(os.str(), fmt::format_int(max_value()).str()); + EXPECT_EQ(fmt::format_int(42).str(), "42"); + EXPECT_EQ(fmt::format_int(42).size(), 2u); + EXPECT_EQ(fmt::format_int(-42).str(), "-42"); + EXPECT_EQ(fmt::format_int(-42).size(), 3u); + EXPECT_EQ(fmt::format_int(42ul).str(), "42"); + EXPECT_EQ(fmt::format_int(-42l).str(), "-42"); + EXPECT_EQ(fmt::format_int(42ull).str(), "42"); + EXPECT_EQ(fmt::format_int(-42ll).str(), "-42");\ + EXPECT_EQ(fmt::format_int(max_value()).str(), + std::to_string(max_value())); } #ifndef FMT_STATIC_THOUSANDS_SEPARATOR @@ -2289,10 +2270,10 @@ TEST(format_test, format_named_arg_with_locale) { TEST(format_test, format_locale) { auto loc = std::locale({}, new fmt::format_facet(",")); - EXPECT_EQ("7,5bc,d15", fmt::format(loc, "{:Lx}", 123456789)); - EXPECT_EQ("-0b111,010,110,111,100,110,100,010,101", - fmt::format(loc, "{:#Lb}", -123456789)); - EXPECT_EQ(" 30,071", fmt::format(loc, "{:10Lo}", 12345)); + EXPECT_EQ(fmt::format(loc, "{:Lx}", 123456789), "7,5bc,d15"); + EXPECT_EQ(fmt::format(loc, "{:#Lb}", -123456789), + "-0b111,010,110,111,100,110,100,010,101"); + EXPECT_EQ(fmt::format(loc, "{:10Lo}", 12345), " 30,071"); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR -- cgit v1.2.3 From d5da9cc40eee910be40779dc101c2d9aaaad61e2 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 19 Dec 2023 17:51:41 -0800 Subject: Apply clang-tidy --- include/fmt/args.h | 11 ++-- include/fmt/chrono.h | 133 +++++++++++++++++++++++++---------------------- include/fmt/color.h | 67 +++++++++++++----------- include/fmt/compile.h | 14 ++--- include/fmt/core.h | 12 ++--- include/fmt/format-inl.h | 102 +++++++++++++++++++----------------- include/fmt/format.h | 112 ++++++++++++++++++++------------------- include/fmt/os.h | 32 ++++++------ include/fmt/ostream.h | 7 +-- include/fmt/ranges.h | 16 +++--- include/fmt/std.h | 2 +- include/fmt/xchar.h | 7 +-- 12 files changed, 274 insertions(+), 241 deletions(-) diff --git a/include/fmt/args.h b/include/fmt/args.h index 2d684e7c..ad1654bb 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -22,8 +22,9 @@ template struct is_reference_wrapper : std::false_type {}; template struct is_reference_wrapper> : std::true_type {}; -template const T& unwrap(const T& v) { return v; } -template const T& unwrap(const std::reference_wrapper& v) { +template auto unwrap(const T& v) -> const T& { return v; } +template +auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(v); } @@ -50,7 +51,7 @@ class dynamic_arg_list { std::unique_ptr> head_; public: - template const T& push(const Arg& arg) { + template auto push(const Arg& arg) -> const T& { auto new_node = std::unique_ptr>(new typed_node(arg)); auto& value = new_node->value; new_node->next = std::move(head_); @@ -110,14 +111,14 @@ class dynamic_format_arg_store friend class basic_format_args; - unsigned long long get_types() const { + auto get_types() const -> unsigned long long { return detail::is_unpacked_bit | data_.size() | (named_info_.empty() ? 0ULL : static_cast(detail::has_named_args_bit)); } - const basic_format_arg* data() const { + auto data() const -> const basic_format_arg* { return named_info_.empty() ? data_.data() : data_.data() + 1; } diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index bed584b6..1a4b7d5a 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -72,7 +72,8 @@ template ::value && std::numeric_limits::is_signed == std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -101,7 +102,8 @@ template ::value && std::numeric_limits::is_signed != std::numeric_limits::is_signed)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; using F = std::numeric_limits; using T = std::numeric_limits; @@ -133,7 +135,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) + -> To { ec = 0; return from; } // function @@ -154,7 +157,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { // clang-format on template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; using T = std::numeric_limits; static_assert(std::is_floating_point::value, "From must be floating"); @@ -176,7 +179,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { template ::value)> -FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { +FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { ec = 0; static_assert(std::is_floating_point::value, "From must be floating"); return from; @@ -188,8 +191,8 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) { template ::value), FMT_ENABLE_IF(std::is_integral::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { using From = std::chrono::duration; ec = 0; // the basic idea is that we need to convert from count() in the from type @@ -240,8 +243,8 @@ To safe_duration_cast(std::chrono::duration from, template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> -To safe_duration_cast(std::chrono::duration from, - int& ec) { +auto safe_duration_cast(std::chrono::duration from, + int& ec) -> To { using From = std::chrono::duration; ec = 0; if (std::isnan(from.count())) { @@ -321,12 +324,12 @@ To safe_duration_cast(std::chrono::duration from, namespace detail { template struct null {}; -inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } -inline null<> localtime_s(...) { return null<>(); } -inline null<> gmtime_r(...) { return null<>(); } -inline null<> gmtime_s(...) { return null<>(); } +inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } +inline auto localtime_s(...) -> null<> { return null<>(); } +inline auto gmtime_r(...) -> null<> { return null<>(); } +inline auto gmtime_s(...) -> null<> { return null<>(); } -inline const std::locale& get_classic_locale() { +inline auto get_classic_locale() -> const std::locale& { static const auto& locale = std::locale::classic(); return locale; } @@ -442,7 +445,7 @@ struct is_same_arithmetic_type template < typename To, typename FromRep, typename FromPeriod, FMT_ENABLE_IF(is_same_arithmetic_type::value)> -To fmt_duration_cast(std::chrono::duration from) { +auto fmt_duration_cast(std::chrono::duration from) -> To { #if FMT_SAFE_DURATION_CAST // Throwing version of safe_duration_cast is only available for // integer to integer or float to float casts. @@ -459,14 +462,15 @@ To fmt_duration_cast(std::chrono::duration from) { template < typename To, typename FromRep, typename FromPeriod, FMT_ENABLE_IF(!is_same_arithmetic_type::value)> -To fmt_duration_cast(std::chrono::duration from) { +auto fmt_duration_cast(std::chrono::duration from) -> To { // Mixed integer <-> float cast is not supported by safe_duration_cast. return std::chrono::duration_cast(from); } template -std::time_t to_time_t( - std::chrono::time_point time_point) { +auto to_time_t( + std::chrono::time_point time_point) + -> std::time_t { // Cannot use std::chrono::system_clock::to_time_t since this would first // require a cast to std::chrono::system_clock::time_point, which could // overflow. @@ -483,29 +487,29 @@ FMT_BEGIN_EXPORT expressed in local time. Unlike ``std::localtime``, this function is thread-safe on most platforms. */ -inline std::tm localtime(std::time_t time) { +inline auto localtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; dispatcher(std::time_t t) : time_(t) {} - bool run() { + auto run() -> bool { using namespace fmt::detail; return handle(localtime_r(&time_, &tm_)); } - bool handle(std::tm* tm) { return tm != nullptr; } + auto handle(std::tm* tm) -> bool { return tm != nullptr; } - bool handle(detail::null<>) { + auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(localtime_s(&tm_, &time_)); } - bool fallback(int res) { return res == 0; } + auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - bool fallback(detail::null<>) { + auto fallback(detail::null<>) -> bool { using namespace fmt::detail; std::tm* tm = std::localtime(&time_); if (tm) tm_ = *tm; @@ -532,29 +536,29 @@ inline auto localtime(std::chrono::local_time time) -> std::tm { expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this function is thread-safe on most platforms. */ -inline std::tm gmtime(std::time_t time) { +inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; dispatcher(std::time_t t) : time_(t) {} - bool run() { + auto run() -> bool { using namespace fmt::detail; return handle(gmtime_r(&time_, &tm_)); } - bool handle(std::tm* tm) { return tm != nullptr; } + auto handle(std::tm* tm) -> bool { return tm != nullptr; } - bool handle(detail::null<>) { + auto handle(detail::null<>) -> bool { using namespace fmt::detail; return fallback(gmtime_s(&tm_, &time_)); } - bool fallback(int res) { return res == 0; } + auto fallback(int res) -> bool { return res == 0; } #if !FMT_MSC_VERSION - bool fallback(detail::null<>) { + auto fallback(detail::null<>) -> bool { std::tm* tm = std::gmtime(&time_); if (tm) tm_ = *tm; return tm != nullptr; @@ -568,8 +572,9 @@ inline std::tm gmtime(std::time_t time) { } template -inline std::tm gmtime( - std::chrono::time_point time_point) { +inline auto gmtime( + std::chrono::time_point time_point) + -> std::tm { return gmtime(detail::to_time_t(time_point)); } @@ -609,7 +614,8 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, } } -template FMT_CONSTEXPR inline const char* get_units() { +template +FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "as"; if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; @@ -665,9 +671,8 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt { // Parses a put_time-like format string and invokes handler actions. template -FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, - const Char* end, - Handler&& handler) { +FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { if (begin == end || *begin == '}') return begin; if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; @@ -998,25 +1003,25 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() {} }; -inline const char* tm_wday_full_name(int wday) { +inline auto tm_wday_full_name(int wday) -> const char* { static constexpr const char* full_name_list[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; } -inline const char* tm_wday_short_name(int wday) { +inline auto tm_wday_short_name(int wday) -> const char* { static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; } -inline const char* tm_mon_full_name(int mon) { +inline auto tm_mon_full_name(int mon) -> const char* { static constexpr const char* full_name_list[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; } -inline const char* tm_mon_short_name(int mon) { +inline auto tm_mon_short_name(int mon) -> const char* { static constexpr const char* short_name_list[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", @@ -1048,7 +1053,7 @@ inline void tzset_once() { // Converts value to Int and checks that it's in the range [0, upper). template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { +inline auto to_nonnegative_int(T value, Int upper) -> Int { if (!std::is_unsigned::value && (value < 0 || to_unsigned(value) > to_unsigned(upper))) { FMT_THROW(fmt::format_error("chrono value is out of range")); @@ -1056,13 +1061,13 @@ inline Int to_nonnegative_int(T value, Int upper) { return static_cast(value); } template ::value)> -inline Int to_nonnegative_int(T value, Int upper) { +inline auto to_nonnegative_int(T value, Int upper) -> Int { if (value < 0 || value > static_cast(upper)) FMT_THROW(format_error("invalid value")); return static_cast(value); } -constexpr long long pow10(std::uint32_t n) { +constexpr auto pow10(std::uint32_t n) -> long long { return n == 0 ? 1 : 10 * pow10(n - 1); } @@ -1358,7 +1363,7 @@ class tm_writer { subsecs_(subsecs), tm_(tm) {} - OutputIt out() const { return out_; } + auto out() const -> OutputIt { return out_; } FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { out_ = copy_str(begin, end, out_); @@ -1641,16 +1646,16 @@ struct chrono_format_checker : null_chrono_spec_handler { template ::value&& has_isfinite::value)> -inline bool isfinite(T) { +inline auto isfinite(T) -> bool { return true; } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return x % static_cast(y); } template ::value)> -inline T mod(T x, int y) { +inline auto mod(T x, int y) -> T { return std::fmod(x, static_cast(y)); } @@ -1667,8 +1672,8 @@ template struct make_unsigned_or_unchanged { template ::value)> -inline std::chrono::duration get_milliseconds( - std::chrono::duration d) { +inline auto get_milliseconds(std::chrono::duration d) + -> std::chrono::duration { // this may overflow and/or the result may not fit in the // target type. #if FMT_SAFE_DURATION_CAST @@ -1690,13 +1695,13 @@ inline std::chrono::duration get_milliseconds( template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int) { +auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt { return write(out, val); } template ::value)> -OutputIt format_duration_value(OutputIt out, Rep val, int precision) { +auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { auto specs = format_specs(); specs.precision = precision; specs.type = precision >= 0 ? presentation_type::fixed_lower @@ -1705,12 +1710,12 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { } template -OutputIt copy_unit(string_view unit, OutputIt out, Char) { +auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt { return std::copy(unit.begin(), unit.end(), out); } template -OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { +auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt { // This works when wchar_t is UTF-32 because units only contain characters // that have the same representation in UTF-16 and UTF-32. utf8_to_utf16 u(unit); @@ -1718,7 +1723,7 @@ OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) { } template -OutputIt format_duration_unit(OutputIt out) { +auto format_duration_unit(OutputIt out) -> OutputIt { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); *out++ = '['; @@ -1790,7 +1795,7 @@ struct chrono_formatter { } // returns true if nan or inf, writes to out. - bool handle_nan_inf() { + auto handle_nan_inf() -> bool { if (isfinite(val)) { return false; } @@ -1807,18 +1812,22 @@ struct chrono_formatter { return true; } - Rep days() const { return static_cast(s.count() / 86400); } - Rep hour() const { return static_cast(mod((s.count() / 3600), 24)); } + auto days() const -> Rep { return static_cast(s.count() / 86400); } + auto hour() const -> Rep { + return static_cast(mod((s.count() / 3600), 24)); + } - Rep hour12() const { + auto hour12() const -> Rep { Rep hour = static_cast(mod((s.count() / 3600), 12)); return hour <= 0 ? 12 : hour; } - Rep minute() const { return static_cast(mod((s.count() / 60), 60)); } - Rep second() const { return static_cast(mod(s.count(), 60)); } + auto minute() const -> Rep { + return static_cast(mod((s.count() / 60), 60)); + } + auto second() const -> Rep { return static_cast(mod(s.count(), 60)); } - std::tm time() const { + auto time() const -> std::tm { auto time = std::tm(); time.tm_hour = to_nonnegative_int(hour(), 24); time.tm_min = to_nonnegative_int(minute(), 60); @@ -2000,7 +2009,7 @@ class weekday { weekday() = default; explicit constexpr weekday(unsigned wd) noexcept : value(static_cast(wd != 7 ? wd : 0)) {} - constexpr unsigned c_encoding() const noexcept { return value; } + constexpr auto c_encoding() const noexcept -> unsigned { return value; } }; class year_month_day {}; diff --git a/include/fmt/color.h b/include/fmt/color.h index 34de634c..367849a8 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -233,7 +233,7 @@ class text_style { FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept : set_foreground_color(), set_background_color(), ems(em) {} - FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { + FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& { if (!set_foreground_color) { set_foreground_color = rhs.set_foreground_color; foreground_color = rhs.foreground_color; @@ -257,29 +257,29 @@ class text_style { return *this; } - friend FMT_CONSTEXPR text_style operator|(text_style lhs, - const text_style& rhs) { + friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs) + -> text_style { return lhs |= rhs; } - FMT_CONSTEXPR bool has_foreground() const noexcept { + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { return set_foreground_color; } - FMT_CONSTEXPR bool has_background() const noexcept { + FMT_CONSTEXPR auto has_background() const noexcept -> bool { return set_background_color; } - FMT_CONSTEXPR bool has_emphasis() const noexcept { + FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { return static_cast(ems) != 0; } - FMT_CONSTEXPR detail::color_type get_foreground() const noexcept { + FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); return foreground_color; } - FMT_CONSTEXPR detail::color_type get_background() const noexcept { + FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); return background_color; } - FMT_CONSTEXPR emphasis get_emphasis() const noexcept { + FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); return ems; } @@ -297,9 +297,11 @@ class text_style { } } - friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept; + friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept + -> text_style; - friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept; + friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept + -> text_style; detail::color_type foreground_color; detail::color_type background_color; @@ -309,16 +311,19 @@ class text_style { }; /** Creates a text style from the foreground (text) color. */ -FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept { +FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept + -> text_style { return text_style(true, foreground); } /** Creates a text style from the background color. */ -FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept { +FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept + -> text_style { return text_style(false, background); } -FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept { +FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept + -> text_style { return text_style(lhs) | rhs; } @@ -384,8 +389,8 @@ template struct ansi_color_escape { } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } - FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; } - FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept { + FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } + FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* { return buffer + std::char_traits::length(buffer); } @@ -400,25 +405,27 @@ template struct ansi_color_escape { out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } - static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept { + static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept + -> bool { return static_cast(em) & static_cast(mask); } }; template -FMT_CONSTEXPR ansi_color_escape make_foreground_color( - detail::color_type foreground) noexcept { +FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept + -> ansi_color_escape { return ansi_color_escape(foreground, "\x1b[38;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_background_color( - detail::color_type background) noexcept { +FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept + -> ansi_color_escape { return ansi_color_escape(background, "\x1b[48;2;"); } template -FMT_CONSTEXPR ansi_color_escape make_emphasis(emphasis em) noexcept { +FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept + -> ansi_color_escape { return ansi_color_escape(em); } @@ -511,9 +518,10 @@ void print(const text_style& ts, const S& format_str, const Args&... args) { } template > -inline std::basic_string vformat( +inline auto vformat( const text_style& ts, const S& format_str, - basic_format_args>> args) { + basic_format_args>> args) + -> std::basic_string { basic_memory_buffer buf; detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); return fmt::to_string(buf); @@ -532,8 +540,8 @@ inline std::basic_string vformat( \endrst */ template > -inline std::basic_string format(const text_style& ts, const S& format_str, - const Args&... args) { +inline auto format(const text_style& ts, const S& format_str, + const Args&... args) -> std::basic_string { return fmt::vformat(ts, detail::to_string_view(format_str), fmt::make_format_args>(args...)); } @@ -543,9 +551,10 @@ inline std::basic_string format(const text_style& ts, const S& format_str, */ template ::value)> -OutputIt vformat_to( - OutputIt out, const text_style& ts, basic_string_view format_str, - basic_format_args>> args) { +auto vformat_to(OutputIt out, const text_style& ts, + basic_string_view format_str, + basic_format_args>> args) + -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, format_str, args); return detail::get_iterator(buf, out); diff --git a/include/fmt/compile.h b/include/fmt/compile.h index a4c7e495..067cb423 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -14,8 +14,8 @@ FMT_BEGIN_NAMESPACE namespace detail { template -FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end, - counting_iterator it) { +FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end, + counting_iterator it) -> counting_iterator { return it + (end - begin); } @@ -57,7 +57,7 @@ struct udl_compiled_string : compiled_string { #endif template -const T& first(const T& value, const Tail&...) { +auto first(const T& value, const Tail&...) -> const T& { return value; } @@ -488,8 +488,8 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { template ::value)> -format_to_n_result format_to_n(OutputIt out, size_t n, - const S& format_str, Args&&... args) { +auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) + -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); format_to(std::back_inserter(buf), format_str, std::forward(args)...); @@ -498,8 +498,8 @@ format_to_n_result format_to_n(OutputIt out, size_t n, template ::value)> -FMT_CONSTEXPR20 size_t formatted_size(const S& format_str, - const Args&... args) { +FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) + -> size_t { return fmt::format_to(detail::counting_iterator(), format_str, args...) .count(); } diff --git a/include/fmt/core.h b/include/fmt/core.h index 3b5bd9ae..ce9f0e70 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -471,15 +471,15 @@ template class basic_string_view { size_ -= n; } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( - basic_string_view sv) const noexcept { + FMT_CONSTEXPR_CHAR_TRAITS auto starts_with( + basic_string_view sv) const noexcept -> bool { return size_ >= sv.size_ && std::char_traits::compare(data_, sv.data_, sv.size_) == 0; } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { + FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(Char c) const noexcept -> bool { return size_ >= 1 && std::char_traits::eq(*data_, c); } - FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { + FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(const Char* s) const -> bool { return starts_with(basic_string_view(s)); } @@ -617,10 +617,10 @@ FMT_TYPE_CONSTANT(const Char*, cstring_type); FMT_TYPE_CONSTANT(basic_string_view, string_type); FMT_TYPE_CONSTANT(const void*, pointer_type); -constexpr bool is_integral_type(type t) { +constexpr auto is_integral_type(type t) -> bool { return t > type::none_type && t <= type::last_integer_type; } -constexpr bool is_arithmetic_type(type t) { +constexpr auto is_arithmetic_type(type t) -> bool { return t > type::none_type && t <= type::last_numeric_type; } diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index bfd00566..bb40e62c 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -85,7 +85,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { static_assert(std::is_same::value, ""); } -template Locale locale_ref::get() const { +template auto locale_ref::get() const -> Locale { static_assert(std::is_same::value, ""); return locale_ ? *static_cast(locale_) : std::locale(); } @@ -97,7 +97,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); return {std::move(grouping), thousands_sep}; } -template FMT_FUNC Char decimal_point_impl(locale_ref loc) { +template +FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { return std::use_facet>(loc.get()) .decimal_point(); } @@ -143,24 +144,25 @@ FMT_API FMT_FUNC auto format_facet::do_put( } #endif -FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, - format_args args) { +FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args) + -> std::system_error { auto ec = std::error_code(error_code, std::generic_category()); return std::system_error(ec, vformat(fmt, args)); } namespace detail { -template inline bool operator==(basic_fp x, basic_fp y) { +template +inline auto operator==(basic_fp x, basic_fp y) -> bool { return x.f == y.f && x.e == y.e; } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept { +FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { +FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -169,14 +171,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept { namespace dragonbox { // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t { return umul128_upper64(static_cast(x) << 32, y); } // Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_lower128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint64_t high = x * y.high(); uint128_fallback high_low = umul128(x, y.low()); return {high + high_low.high(), high_low.low()}; @@ -184,12 +186,12 @@ inline uint128_fallback umul192_lower128(uint64_t x, // Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a // 64-bit unsigned integer. -inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept { +inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t { return x * y; } // Various fast log computations. -inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { +inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); return (e * 631305 - 261663) >> 21; } @@ -203,7 +205,7 @@ FMT_INLINE_VARIABLE constexpr struct { // divisible by pow(10, N). // Precondition: n <= pow(10, N + 1). template -bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { +auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool { // The numbers below are chosen such that: // 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100, // 2. nm mod 2^k < m if and only if n is divisible by d, @@ -228,7 +230,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept { // Computes floor(n / pow(10, N)) for small n and N. // Precondition: n <= pow(10, N + 1). -template uint32_t small_division_by_pow10(uint32_t n) noexcept { +template auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t { constexpr auto info = div_small_pow10_infos[N - 1]; FMT_ASSERT(n <= info.divisor * 10, "n is too large"); constexpr uint32_t magic_number = @@ -237,12 +239,12 @@ template uint32_t small_division_by_pow10(uint32_t n) noexcept { } // Computes floor(n / 10^(kappa + 1)) (float) -inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t { // 1374389535 = ceil(2^37/100) return static_cast((static_cast(n) * 1374389535) >> 37); } // Computes floor(n / 10^(kappa + 1)) (double) -inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept { +inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t { // 2361183241434822607 = ceil(2^(64+7)/1000) return umul128_upper64(n, 2361183241434822607ull) >> 7; } @@ -254,7 +256,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint64_t; - static uint64_t get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); static constexpr const uint64_t pow10_significands[] = { @@ -296,20 +298,23 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul96_upper64(u, cache); return {static_cast(r >> 32), static_cast(r) == 0}; } - static uint32_t compute_delta(const cache_entry_type& cache, - int beta) noexcept { + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept + -> uint32_t { return static_cast(cache >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -318,22 +323,22 @@ template <> struct cache_accessor { static_cast(r >> (32 - beta)) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache - (cache >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return static_cast( (cache + (cache >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta)); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (static_cast( cache >> (64 - num_significand_bits() - 2 - beta)) + 1) / @@ -345,7 +350,7 @@ template <> struct cache_accessor { using carrier_uint = float_info::carrier_uint; using cache_entry_type = uint128_fallback; - static uint128_fallback get_cached_power(int k) noexcept { + static auto get_cached_power(int k) noexcept -> uint128_fallback { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); @@ -1069,19 +1074,22 @@ template <> struct cache_accessor { bool is_integer; }; - static compute_mul_result compute_mul( - carrier_uint u, const cache_entry_type& cache) noexcept { + static auto compute_mul(carrier_uint u, + const cache_entry_type& cache) noexcept + -> compute_mul_result { auto r = umul192_upper128(u, cache); return {r.high(), r.low() == 0}; } - static uint32_t compute_delta(cache_entry_type const& cache, - int beta) noexcept { + static auto compute_delta(cache_entry_type const& cache, int beta) noexcept + -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } - static compute_mul_parity_result compute_mul_parity( - carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept { + static auto compute_mul_parity(carrier_uint two_f, + const cache_entry_type& cache, + int beta) noexcept + -> compute_mul_parity_result { FMT_ASSERT(beta >= 1, ""); FMT_ASSERT(beta < 64, ""); @@ -1090,35 +1098,35 @@ template <> struct cache_accessor { ((r.high() << beta) | (r.low() >> (64 - beta))) == 0}; } - static carrier_uint compute_left_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_left_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() - (cache.high() >> (num_significand_bits() + 2))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_right_endpoint_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_right_endpoint_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return (cache.high() + (cache.high() >> (num_significand_bits() + 1))) >> (64 - num_significand_bits() - 1 - beta); } - static carrier_uint compute_round_up_for_shorter_interval_case( - const cache_entry_type& cache, int beta) noexcept { + static auto compute_round_up_for_shorter_interval_case( + const cache_entry_type& cache, int beta) noexcept -> carrier_uint { return ((cache.high() >> (64 - num_significand_bits() - 2 - beta)) + 1) / 2; } }; -FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { +FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback { return cache_accessor::get_cached_power(k); } // Various integer checks template -bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { +auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_upper_threshold = 3; return exponent >= case_shorter_interval_left_endpoint_lower_threshold && @@ -1232,7 +1240,7 @@ FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { return ret_value; } -template decimal_fp to_decimal(T x) noexcept { +template auto to_decimal(T x) noexcept -> decimal_fp { // Step 1: integer promotion & Schubfach multiplier calculation. using carrier_uint = typename float_info::carrier_uint; @@ -1415,7 +1423,7 @@ FMT_FUNC void report_system_error(int error_code, report_error(format_system_error, error_code, message); } -FMT_FUNC std::string vformat(string_view fmt, format_args args) { +FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. auto buffer = memory_buffer(); @@ -1425,7 +1433,7 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { namespace detail { #if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) -FMT_FUNC bool write_console(int, string_view) { return false; } +FMT_FUNC auto write_console(int, string_view) -> bool { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // diff --git a/include/fmt/format.h b/include/fmt/format.h index 7e1e6926..1ae0039e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -377,8 +377,8 @@ class uint128_fallback { constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {} constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {} - constexpr uint64_t high() const noexcept { return hi_; } - constexpr uint64_t low() const noexcept { return lo_; } + constexpr auto high() const noexcept -> uint64_t { return hi_; } + constexpr auto low() const noexcept -> uint64_t { return lo_; } template ::value)> constexpr explicit operator T() const { @@ -454,7 +454,7 @@ class uint128_fallback { hi_ &= n.hi_; } - FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept { + FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& { if (is_constant_evaluated()) { lo_ += n; hi_ += (lo_ < n ? 1 : 0); @@ -744,7 +744,7 @@ inline auto compute_width(basic_string_view s) -> size_t { } // Computes approximate display width of a UTF-8 string. -FMT_CONSTEXPR inline size_t compute_width(string_view s) { +FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { size_t num_code_points = 0; // It is not a lambda for compatibility with C++14. struct count_code_points { @@ -1042,7 +1042,7 @@ struct is_contiguous> : std::true_type { FMT_END_EXPORT namespace detail { -FMT_API bool write_console(int fd, string_view text); +FMT_API auto write_console(int fd, string_view text) -> bool; FMT_API void print(std::FILE*, string_view); } // namespace detail @@ -1167,7 +1167,7 @@ using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr const char* digits2(size_t value) { +constexpr auto digits2(size_t value) -> const char* { // GCC generates slightly better code when value is pointer-size. return &"0001020304050607080910111213141516171819" "2021222324252627282930313233343536373839" @@ -1177,7 +1177,7 @@ constexpr const char* digits2(size_t value) { } // Sign is a template parameter to workaround a bug in gcc 4.8. -template constexpr Char sign(Sign s) { +template constexpr auto sign(Sign s) -> Char { #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 static_assert(std::is_same::value, ""); #endif @@ -1438,22 +1438,23 @@ template class to_utf8 { : "invalid utf32")); } operator string_view() const { return string_view(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char* c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } + auto size() const -> size_t { return buffer_.size() - 1; } + auto c_str() const -> const char* { return &buffer_[0]; } + auto str() const -> std::string { return std::string(&buffer_[0], size()); } // Performs conversion returning a bool instead of throwing exception on // conversion error. This method may still throw in case of memory allocation // error. - bool convert(basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + auto convert(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { if (!convert(buffer_, s, policy)) return false; buffer_.push_back(0); return true; } - static bool convert( - Buffer& buf, basic_string_view s, - to_utf8_error_policy policy = to_utf8_error_policy::abort) { + static auto convert(Buffer& buf, basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) + -> bool { for (auto p = s.begin(); p != s.end(); ++p) { uint32_t c = static_cast(*p); if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { @@ -1489,7 +1490,7 @@ template class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { +inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return {static_cast(p >> 64), static_cast(p)}; @@ -1520,19 +1521,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept { namespace dragonbox { // Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from // https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1. -inline int floor_log10_pow2(int e) noexcept { +inline auto floor_log10_pow2(int e) noexcept -> int { FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent"); static_assert((-1 >> 1) == -1, "right shift is not arithmetic"); return (e * 315653) >> 20; } -inline int floor_log2_pow10(int e) noexcept { +inline auto floor_log2_pow10(int e) noexcept -> int { FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); return (e * 1741647) >> 19; } // Computes upper 64 bits of multiplication of two 64-bit unsigned integers. -inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { +inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return static_cast(p >> 64); @@ -1545,14 +1546,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept { // Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a // 128-bit unsigned integer. -inline uint128_fallback umul192_upper128(uint64_t x, - uint128_fallback y) noexcept { +inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept + -> uint128_fallback { uint128_fallback r = umul128(x, y.high()); r += umul128_upper64(x, y.low()); return r; } -FMT_API uint128_fallback get_cached_power(int k) noexcept; +FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback; // Type-specific information that Dragonbox uses. template struct float_info; @@ -1606,14 +1607,14 @@ template FMT_API auto to_decimal(T x) noexcept -> decimal_fp; } // namespace dragonbox // Returns true iff Float has the implicit bit which is not stored. -template constexpr bool has_implicit_bit() { +template constexpr auto has_implicit_bit() -> bool { // An 80-bit FP number has a 64-bit significand an no implicit bit. return std::numeric_limits::digits != 64; } // Returns the number of significand bits stored in Float. The implicit bit is // not counted since it is not stored. -template constexpr int num_significand_bits() { +template constexpr auto num_significand_bits() -> int { // std::numeric_limits may not support __float128. return is_float128() ? 112 : (std::numeric_limits::digits - @@ -1706,7 +1707,7 @@ using fp = basic_fp; // Normalizes the value converted from double and multiplied by (1 << SHIFT). template -FMT_CONSTEXPR basic_fp normalize(basic_fp value) { +FMT_CONSTEXPR auto normalize(basic_fp value) -> basic_fp { // Handle subnormals. const auto implicit_bit = F(1) << num_significand_bits(); const auto shifted_implicit_bit = implicit_bit << SHIFT; @@ -1723,7 +1724,7 @@ FMT_CONSTEXPR basic_fp normalize(basic_fp value) { } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t { #if FMT_USE_INT128 auto product = static_cast<__uint128_t>(lhs) * rhs; auto f = static_cast(product >> 64); @@ -1740,7 +1741,7 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { #endif } -FMT_CONSTEXPR inline fp operator*(fp x, fp y) { +FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp { return {multiply(x.f, y.f), x.e + y.e + 64}; } @@ -2057,10 +2058,10 @@ template class digit_grouping { std::string::const_iterator group; int pos; }; - next_state initial_state() const { return {grouping_.begin(), 0}; } + auto initial_state() const -> next_state { return {grouping_.begin(), 0}; } // Returns the next digit group separator position. - int next(next_state& state) const { + auto next(next_state& state) const -> int { if (thousands_sep_.empty()) return max_value(); if (state.group == grouping_.end()) return state.pos += grouping_.back(); if (*state.group <= 0 || *state.group == max_value()) @@ -2079,9 +2080,9 @@ template class digit_grouping { digit_grouping(std::string grouping, std::basic_string sep) : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} - bool has_separator() const { return !thousands_sep_.empty(); } + auto has_separator() const -> bool { return !thousands_sep_.empty(); } - int count_separators(int num_digits) const { + auto count_separators(int num_digits) const -> int { int count = 0; auto state = initial_state(); while (num_digits > next(state)) ++count; @@ -2090,7 +2091,7 @@ template class digit_grouping { // Applies grouping to digits and write the output to out. template - Out apply(Out out, basic_string_view digits) const { + auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); auto separators = basic_memory_buffer(); separators.push_back(0); @@ -2331,25 +2332,25 @@ class counting_iterator { FMT_CONSTEXPR counting_iterator() : count_(0) {} - FMT_CONSTEXPR size_t count() const { return count_; } + FMT_CONSTEXPR auto count() const -> size_t { return count_; } - FMT_CONSTEXPR counting_iterator& operator++() { + FMT_CONSTEXPR auto operator++() -> counting_iterator& { ++count_; return *this; } - FMT_CONSTEXPR counting_iterator operator++(int) { + FMT_CONSTEXPR auto operator++(int) -> counting_iterator { auto it = *this; ++*this; return it; } - FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, - difference_type n) { + FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n) + -> counting_iterator { it.count_ += static_cast(n); return it; } - FMT_CONSTEXPR value_type operator*() const { return {}; } + FMT_CONSTEXPR auto operator*() const -> value_type { return {}; } }; template @@ -2750,12 +2751,12 @@ template class fallback_digit_grouping { public: constexpr fallback_digit_grouping(locale_ref, bool) {} - constexpr bool has_separator() const { return false; } + constexpr auto has_separator() const -> bool { return false; } - constexpr int count_separators(int) const { return 0; } + constexpr auto count_separators(int) const -> int { return 0; } template - constexpr Out apply(Out out, basic_string_view) const { + constexpr auto apply(Out out, basic_string_view) const -> Out { return out; } }; @@ -2774,7 +2775,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, } } -template constexpr bool isnan(T value) { +template constexpr auto isnan(T value) -> bool { return !(value >= value); // std::isnan doesn't support __float128. } @@ -2787,14 +2788,14 @@ struct has_isfinite> template ::value&& has_isfinite::value)> -FMT_CONSTEXPR20 bool isfinite(T value) { +FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) return !detail::isnan(value) && value < inf && value > -inf; return std::isfinite(value); } template ::value)> -FMT_CONSTEXPR bool isfinite(T value) { +FMT_CONSTEXPR auto isfinite(T value) -> bool { T inf = T(std::numeric_limits::infinity()); // std::isfinite doesn't support __float128. return !detail::isnan(value) && value < inf && value > -inf; @@ -2831,10 +2832,10 @@ class bigint { basic_memory_buffer bigits_; int exp_; - FMT_CONSTEXPR20 bigit operator[](int index) const { + FMT_CONSTEXPR20 auto operator[](int index) const -> bigit { return bigits_[to_unsigned(index)]; } - FMT_CONSTEXPR20 bigit& operator[](int index) { + FMT_CONSTEXPR20 auto operator[](int index) -> bigit& { return bigits_[to_unsigned(index)]; } @@ -2930,11 +2931,11 @@ class bigint { assign(uint64_or_128_t(n)); } - FMT_CONSTEXPR20 int num_bigits() const { + FMT_CONSTEXPR20 auto num_bigits() const -> int { return static_cast(bigits_.size()) + exp_; } - FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { + FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -2949,13 +2950,15 @@ class bigint { return *this; } - template FMT_CONSTEXPR20 bigint& operator*=(Int value) { + template + FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { + friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs) + -> int { int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); if (num_lhs_bigits != num_rhs_bigits) return num_lhs_bigits > num_rhs_bigits ? 1 : -1; @@ -2972,8 +2975,9 @@ class bigint { } // Returns compare(lhs1 + lhs2, rhs). - friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { + friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1, + const bigint& lhs2, const bigint& rhs) + -> int { auto minimum = [](int a, int b) { return a < b ? a : b; }; auto maximum = [](int a, int b) { return a > b ? a : b; }; int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits()); @@ -3060,7 +3064,7 @@ class bigint { // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { + FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -3303,7 +3307,7 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, format_hexfloat(static_cast(value), precision, specs, buf); } -constexpr uint32_t fractional_part_rounding_thresholds(int index) { +constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { // For checking rounding thresholds. // The kth entry is chosen to be the smallest integer such that the // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. diff --git a/include/fmt/os.h b/include/fmt/os.h index 462ac44d..c81955d7 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -117,7 +117,7 @@ template class basic_cstring_view { basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} /** Returns the pointer to a C string. */ - const Char* c_str() const { return data_; } + auto c_str() const -> const Char* { return data_; } }; using cstring_view = basic_cstring_view; @@ -172,7 +172,7 @@ std::system_error windows_error(int error_code, string_view message, // Can be used to report errors from destructors. FMT_API void report_windows_error(int error_code, const char* message) noexcept; #else -inline const std::error_category& system_category() noexcept { +inline auto system_category() noexcept -> const std::error_category& { return std::system_category(); } #endif // _WIN32 @@ -209,7 +209,7 @@ class buffered_file { other.file_ = nullptr; } - buffered_file& operator=(buffered_file&& other) { + auto operator=(buffered_file&& other) -> buffered_file& { close(); file_ = other.file_; other.file_ = nullptr; @@ -223,9 +223,9 @@ class buffered_file { FMT_API void close(); // Returns the pointer to a FILE object representing this file. - FILE* get() const noexcept { return file_; } + auto get() const noexcept -> FILE* { return file_; } - FMT_API int descriptor() const; + FMT_API auto descriptor() const -> int; void vprint(string_view format_str, format_args args) { fmt::vprint(file_, format_str, args); @@ -275,7 +275,7 @@ class FMT_API file { file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } // Move assignment is not noexcept because close may throw. - file& operator=(file&& other) { + auto operator=(file&& other) -> file& { close(); fd_ = other.fd_; other.fd_ = -1; @@ -286,24 +286,24 @@ class FMT_API file { ~file() noexcept; // Returns the file descriptor. - int descriptor() const noexcept { return fd_; } + auto descriptor() const noexcept -> int { return fd_; } // Closes the file. void close(); // Returns the file size. The size has signed type for consistency with // stat::st_size. - long long size() const; + auto size() const -> long long; // Attempts to read count bytes from the file into the specified buffer. - size_t read(void* buffer, size_t count); + auto read(void* buffer, size_t count) -> size_t; // Attempts to write count bytes from the specified buffer to the file. - size_t write(const void* buffer, size_t count); + auto write(const void* buffer, size_t count) -> size_t; // Duplicates a file descriptor with the dup function and returns // the duplicate as a file object. - static file dup(int fd); + static auto dup(int fd) -> file; // Makes fd be the copy of this file descriptor, closing fd first if // necessary. @@ -319,7 +319,7 @@ class FMT_API file { // Creates a buffered_file object associated with this file and detaches // this file object from the file. - buffered_file fdopen(const char* mode); + auto fdopen(const char* mode) -> buffered_file; # if defined(_WIN32) && !defined(__MINGW32__) // Opens a file and constructs a file object representing this file by @@ -329,14 +329,14 @@ class FMT_API file { }; // Returns the memory page size. -long getpagesize(); +auto getpagesize() -> long; namespace detail { struct buffer_size { buffer_size() = default; size_t value = 0; - buffer_size operator=(size_t val) const { + auto operator=(size_t val) const -> buffer_size { auto bs = buffer_size(); bs.value = val; return bs; @@ -413,7 +413,7 @@ class FMT_API ostream { void flush() { buffer_.flush(); } template - friend ostream output_file(cstring_view path, T... params); + friend auto output_file(cstring_view path, T... params) -> ostream; void close() { buffer_.close(); } @@ -443,7 +443,7 @@ class FMT_API ostream { \endrst */ template -inline ostream output_file(cstring_view path, T... params) { +inline auto output_file(cstring_view path, T... params) -> ostream { return {path, detail::ostream_params(params...)}; } #endif // FMT_USE_FCNTL diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 3086191a..3eb62143 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -40,7 +40,8 @@ template class file_access FILE*; #endif -inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { +inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) + -> bool { FILE* f = nullptr; #if FMT_MSC_VERSION if (auto* buf = dynamic_cast(os.rdbuf())) @@ -69,8 +70,8 @@ inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { #endif return false; } -inline bool write_ostream_unicode(std::wostream&, - fmt::basic_string_view) { +inline auto write_ostream_unicode(std::wostream&, + fmt::basic_string_view) -> bool { return false; } diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index c0b51aee..d4501529 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -183,7 +183,7 @@ template using make_index_sequence = std::make_index_sequence; template struct integer_sequence { using value_type = T; - static FMT_CONSTEXPR size_t size() { return sizeof...(N); } + static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); } }; template using index_sequence = integer_sequence; @@ -207,15 +207,15 @@ class is_tuple_formattable_ { }; template class is_tuple_formattable_ { template - static std::true_type check2(index_sequence, - integer_sequence); - static std::false_type check2(...); + static auto check2(index_sequence, + integer_sequence) -> std::true_type; + static auto check2(...) -> std::false_type; template - static decltype(check2( + static auto check(index_sequence) -> decltype(check2( index_sequence{}, - integer_sequence< - bool, (is_formattable::type, - C>::value)...>{})) check(index_sequence); + integer_sequence::type, + C>::value)...>{})); public: static constexpr const bool value = diff --git a/include/fmt/std.h b/include/fmt/std.h index 5bafcefb..a9a9dc33 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -176,7 +176,7 @@ struct formatter, Char> : nested_formatter { const std::bitset& bs; template - FMT_CONSTEXPR OutputIt operator()(OutputIt out) { + FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { for (auto pos = N; pos > 0; --pos) { out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); } diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 9a7aa4d7..f609c5c4 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -63,14 +63,15 @@ template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; template -constexpr format_arg_store make_wformat_args( - const T&... args) { +constexpr auto make_wformat_args(const T&... args) + -> format_arg_store { return {args...}; } inline namespace literals { #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS -constexpr detail::udl_arg operator""_a(const wchar_t* s, size_t) { +constexpr auto operator""_a(const wchar_t* s, size_t) + -> detail::udl_arg { return {s}; } #endif -- cgit v1.2.3 From 9cd2b87e18349d8c271f271e8ccf405568ed04aa Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Wed, 20 Dec 2023 21:59:26 +0500 Subject: Fix wchar_t to char conversion warnings (#3764) Signed-off-by: Vladislav Shchapov --- include/fmt/format.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 1ae0039e..dd04a79e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2131,7 +2131,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, case presentation_type::none: case presentation_type::dec: { num_digits = count_digits(value); - format_decimal(appender(buffer), value, num_digits); + format_decimal(appender(buffer), value, num_digits); break; } case presentation_type::hex_lower: @@ -2140,7 +2140,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, if (specs.alt) prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); num_digits = count_digits<4>(value); - format_uint<4, Char>(appender(buffer), value, num_digits, upper); + format_uint<4, char>(appender(buffer), value, num_digits, upper); break; } case presentation_type::bin_lower: @@ -2149,7 +2149,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, if (specs.alt) prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); num_digits = count_digits<1>(value); - format_uint<1, Char>(appender(buffer), value, num_digits); + format_uint<1, char>(appender(buffer), value, num_digits); break; } case presentation_type::oct: { @@ -2158,7 +2158,7 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // is not greater than the number of digits. if (specs.alt && specs.precision <= num_digits && value != 0) prefix_append(prefix, '0'); - format_uint<3, Char>(appender(buffer), value, num_digits); + format_uint<3, char>(appender(buffer), value, num_digits); break; } case presentation_type::chr: -- cgit v1.2.3 From dbd9c89b3c1d4a92f15ab8296faec4507cf50ffb Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 20 Dec 2023 14:59:29 -0800 Subject: Fix URI --- support/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/AndroidManifest.xml b/support/AndroidManifest.xml index b5281fee..c282ef5a 100644 --- a/support/AndroidManifest.xml +++ b/support/AndroidManifest.xml @@ -1 +1 @@ - + -- cgit v1.2.3 From 1ca1a4a7a9f6250d441f98917a30ebd9f409b3dd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 22 Dec 2023 06:35:36 -0800 Subject: Update scan test --- test/scan-test.cc | 4 +-- test/scan.h | 90 +++++++++++++++++++++++++++++++++---------------------- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 25f76109..84603cf1 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -122,11 +122,11 @@ TEST(scan_test, example) { TEST(scan_test, file) { fmt::file read_end, write_end; fmt::file::pipe(read_end, write_end); - fmt::string_view input = "4"; + fmt::string_view input = "42"; write_end.write(input.data(), input.size()); write_end.close(); int value = 0; fmt::scan(read_end.fdopen("r").get(), "{}", value); - EXPECT_EQ(value, 4); + EXPECT_EQ(value, 42); } #endif // FMT_USE_FCNTL diff --git a/test/scan.h b/test/scan.h index 6a44b646..db949388 100644 --- a/test/scan.h +++ b/test/scan.h @@ -48,7 +48,7 @@ class scan_buffer { } // Fills the buffer with more input if available. - virtual void fill() = 0; + virtual void consume() = 0; public: scan_buffer(const scan_buffer&) = delete; @@ -121,17 +121,15 @@ class scan_buffer { auto try_consume() -> bool { FMT_ASSERT(ptr_ != end_, ""); ++ptr_; - if (ptr_ == end_) { - // TODO: refill buffer - return false; - } - return true; + if (ptr_ != end_) return true; + consume(); + return ptr_ != end_; } }; class string_scan_buffer : public scan_buffer { private: - void fill() override {} + void consume() override {} public: explicit string_scan_buffer(string_view s) @@ -142,37 +140,55 @@ class file_scan_buffer : public scan_buffer { private: FILE* file_; char next_; + bool filled_ = false; + // Returns the file's read buffer as a string_view. template - void set_buffer(int, F* f) { - const char* ptr = reinterpret_cast(f->_p); - this->set(ptr, ptr + f->_r); + auto get_buffer(F* file) -> string_view { // Apple libc + char* ptr = reinterpret_cast(file->_p); + return {ptr, to_unsigned(file->_r)}; } - void set_buffer(int c, ...) { - if (c == EOF) return; - next_ = static_cast(c); - this->set(&next_, &next_ + 1); + auto get_buffer(...) -> string_view { + return {&next_, (filled_ ? 1u : 0u)}; + } + + void do_fill() { + string_view buf = get_buffer(file_); + if (buf.size() == 0) { + int result = getc(file_); + if (result != EOF) { + // Put the character back since we are only filling the buffer. + if (ungetc(result, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); + next_ = static_cast(result); + filled_ = true; + } else { + if (ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); + filled_ = false; + } + buf = get_buffer(file_); + } + this->set(buf.begin(), buf.end()); } - void fill() override { - int result = getc(file_); - if (result == EOF) { - if (ferror(file_) != 0) + void consume() override { + // Consume the current buffer content. + string_view buf = get_buffer(file_); + for (size_t i = 0, n = buf.size(); i != n; ++i) { + int result = getc(file_); + if (result == EOF && ferror(file_) != 0) FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); - return; } - // Put the character back since we are only filling the buffer. - if (ungetc(result, file_) == EOF) - FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); - set_buffer(result, file_); + filled_ = false; + do_fill(); } public: explicit file_scan_buffer(FILE* f) : scan_buffer(nullptr, nullptr, false), file_(f) { // TODO: lock file? - set_buffer(EOF, f); - if (is_empty()) fill(); + do_fill(); } }; } // namespace detail @@ -303,12 +319,14 @@ struct scan_handler : error_handler { template auto read_uint() -> T { T value = 0; auto it = scan_ctx_.begin(), end = scan_ctx_.end(); - while (it != end) { - char c = *it++; - if (c < '0' || c > '9') on_error("invalid input"); - // TODO: check overflow + char c = it != end ? *it : '\0'; + if (c < '0' || c > '9') on_error("invalid input"); + do { value = value * 10 + static_cast(c - '0'); - } + c = *++it; + if (c < '0' || c > '9') break; + // TODO: check overflow + } while (it != end); scan_ctx_.advance_to(it); return value; } @@ -316,11 +334,13 @@ struct scan_handler : error_handler { template auto read_int() -> T { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); bool negative = it != end && *it == '-'; - if (negative) ++it; - scan_ctx_.advance_to(it); - const auto value = read_uint::type>(); - if (negative) return -static_cast(value); - return static_cast(value); + if (negative) { + ++it; + scan_ctx_.advance_to(it); + } + auto abs_value = read_uint::type>(); + auto value = static_cast(abs_value); + return negative ? -value : value; } public: -- cgit v1.2.3 From 3a0f4af4e992703ea738a1660f6cc5d60a784ea3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 22 Dec 2023 10:50:01 -0800 Subject: Refactor file layer --- test/scan-test.cc | 2 + test/scan.h | 123 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 89 insertions(+), 36 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 84603cf1..993a4b3f 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -28,6 +28,8 @@ TEST(scan_test, read_int) { EXPECT_EQ(n, 42); fmt::scan("-42", "{}", n); EXPECT_EQ(n, -42); + EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n), + fmt::format_error, "number is too big"); } TEST(scan_test, read_longlong) { diff --git a/test/scan.h b/test/scan.h index db949388..d5831528 100644 --- a/test/scan.h +++ b/test/scan.h @@ -136,51 +136,90 @@ class string_scan_buffer : public scan_buffer { : scan_buffer(s.begin(), s.end(), true) {} }; -class file_scan_buffer : public scan_buffer { - private: - FILE* file_; - char next_; - bool filled_ = false; +// A FILE wrapper. F is FILE defined as a template parameter to make +// system-specific API detection work. +template class file_base { + protected: + F* file_; + + public: + file_base(F* file) : file_(file) {} + operator F*() const { return file_; } + + // Reads a code unit from the stream. + auto get() -> int { + int result = getc(file_); + if (result == EOF && ferror(file_) != 0) + FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); + return result; + } + + // Puts the code unit back into the stream buffer. + void unget(char c) { + if (ungetc(c, file_) == EOF) + FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); + } +}; + +// A FILE wrapper for Apple's libc. +template class apple_file : public file_base { + public: + using file_base::file_base; // Returns the file's read buffer as a string_view. - template - auto get_buffer(F* file) -> string_view { // Apple libc - char* ptr = reinterpret_cast(file->_p); - return {ptr, to_unsigned(file->_r)}; + auto buffer() const -> string_view { + return {reinterpret_cast(this->file_->_p), + to_unsigned(this->file_->_r)}; } - auto get_buffer(...) -> string_view { - return {&next_, (filled_ ? 1u : 0u)}; +}; + +// A fallback FILE wrapper. +template class fallback_file : public file_base { + private: + char next_; // The next unconsumed character in the buffer. + bool has_next_ = false; + + public: + using file_base::file_base; + + auto buffer() const -> string_view { return {&next_, has_next_ ? 1u : 0u}; } + + auto get() -> int { + has_next_ = false; + return file_base::get(); } + void unget(char c) { + file_base::unget(c); + next_ = c; + has_next_ = true; + } +}; + +template +auto get_file(F* file, int) -> apple_file { + return file; +} +auto get_file(FILE* file, ...) -> fallback_file { return file; } + +class file_scan_buffer : public scan_buffer { + private: + decltype(get_file(static_cast(nullptr), 0)) file_; + void do_fill() { - string_view buf = get_buffer(file_); + string_view buf = file_.buffer(); if (buf.size() == 0) { - int result = getc(file_); - if (result != EOF) { - // Put the character back since we are only filling the buffer. - if (ungetc(result, file_) == EOF) - FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); - next_ = static_cast(result); - filled_ = true; - } else { - if (ferror(file_) != 0) - FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); - filled_ = false; - } - buf = get_buffer(file_); + int c = file_.get(); + // Put the character back since we are only filling the buffer. + if (c != EOF) file_.unget(static_cast(c)); + buf = file_.buffer(); } this->set(buf.begin(), buf.end()); } void consume() override { // Consume the current buffer content. - string_view buf = get_buffer(file_); - for (size_t i = 0, n = buf.size(); i != n; ++i) { - int result = getc(file_); - if (result == EOF && ferror(file_) != 0) - FMT_THROW(system_error(errno, FMT_STRING("I/O error"))); - } - filled_ = false; + for (size_t i = 0, n = file_.buffer().size(); i != n; ++i) file_.get(); do_fill(); } @@ -317,18 +356,30 @@ struct scan_handler : error_handler { scan_arg arg_; template auto read_uint() -> T { - T value = 0; auto it = scan_ctx_.begin(), end = scan_ctx_.end(); - char c = it != end ? *it : '\0'; + char c = it != end ? *it : '\0', prev_digit; if (c < '0' || c > '9') on_error("invalid input"); + + int num_digits = 0; + T value = 0, prev = 0; do { + prev = value; value = value * 10 + static_cast(c - '0'); + prev_digit = c; c = *++it; + ++num_digits; if (c < '0' || c > '9') break; - // TODO: check overflow } while (it != end); scan_ctx_.advance_to(it); - return value; + + // Check overflow. + if (num_digits <= std::numeric_limits::digits10) return value; + const unsigned max = to_unsigned((std::numeric_limits::max)()); + if (num_digits == std::numeric_limits::digits10 + 1 && + prev * 10ull + unsigned(prev_digit - '0') <= max) { + return value; + } + throw format_error("number is too big"); } template auto read_int() -> T { -- cgit v1.2.3 From 44dd6c0e099b16ab290428c615e07aeab430e86c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 22 Dec 2023 13:39:18 -0800 Subject: Add glibc support to scan --- test/scan.h | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/scan.h b/test/scan.h index d5831528..c48357fc 100644 --- a/test/scan.h +++ b/test/scan.h @@ -161,6 +161,17 @@ template class file_base { } }; +// A FILE wrapper for glibc. +template class glibc_file : public file_base { + public: + using file_base::file_base; + + auto buffer() const -> string_view { + return {this->file_->_IO_read_ptr, + to_unsigned(this->file_->_IO_read_end - this->file_->_IO_read_ptr)}; + } +}; + // A FILE wrapper for Apple's libc. template class apple_file : public file_base { public: @@ -196,6 +207,10 @@ template class fallback_file : public file_base { } }; +template +auto get_file(F* file, int) -> glibc_file { + return file; +} template auto get_file(F* file, int) -> apple_file { return file; @@ -206,7 +221,7 @@ class file_scan_buffer : public scan_buffer { private: decltype(get_file(static_cast(nullptr), 0)) file_; - void do_fill() { + void fill() { string_view buf = file_.buffer(); if (buf.size() == 0) { int c = file_.get(); @@ -220,14 +235,14 @@ class file_scan_buffer : public scan_buffer { void consume() override { // Consume the current buffer content. for (size_t i = 0, n = file_.buffer().size(); i != n; ++i) file_.get(); - do_fill(); + fill(); } public: explicit file_scan_buffer(FILE* f) : scan_buffer(nullptr, nullptr, false), file_(f) { // TODO: lock file? - do_fill(); + fill(); } }; } // namespace detail -- cgit v1.2.3 From b5f6b36b00b933ad384aaf37f0c5b46ec4efb831 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 22 Dec 2023 16:55:02 -0800 Subject: Update changelog --- ChangeLog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 2e51d107..87ec5278 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,13 @@ +# 10.2.0 - TBD + +- Fixed various warnings and compilation issues + (https://github.com/fmtlib/fmt/pull/3624). Thanks @vinayyadav3016. + +- Updated CI dependencies + (https://github.com/fmtlib/fmt/pull/3615, + https://github.com/fmtlib/fmt/pull/3622, + https://github.com/fmtlib/fmt/pull/3623). + # 10.1.1 - 2023-08-28 - Added formatters for `std::atomic` and `atomic_flag` -- cgit v1.2.3 From 968fb9d1661f73bbe81f1661fb6aeac777e77e47 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 22 Dec 2023 20:42:20 -0800 Subject: Update changelog --- ChangeLog.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 87ec5278..fc2eef43 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,7 +1,18 @@ # 10.2.0 - TBD +- Only export `format_error` when {fmt} is built as a shared library + (https://github.com/fmtlib/fmt/issues/3626, + https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. + +- Added an option to build without `wchar_t` support on Windows + (https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. + - Fixed various warnings and compilation issues - (https://github.com/fmtlib/fmt/pull/3624). Thanks @vinayyadav3016. + (https://github.com/fmtlib/fmt/pull/3610, + https://github.com/fmtlib/fmt/pull/3624, + https://github.com/fmtlib/fmt/pull/3630, + https://github.com/fmtlib/fmt/pull/3634). + Thanks @danakj, @vinayyadav3016, @cyyever, @phprus. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3615, @@ -23,8 +34,7 @@ https://github.com/fmtlib/fmt/pull/3605). Thanks @MathewBensonCode. - Made `fmt::to_string` work with types that have `format_as` - overloads (https://github.com/fmtlib/fmt/pull/3575). - Thanks @phprus. + overloads (https://github.com/fmtlib/fmt/pull/3575). Thanks @phprus. - Made `formatted_size` work with integral format specifiers at compile time (https://github.com/fmtlib/fmt/pull/3591). Thanks @elbeno. -- cgit v1.2.3 From 56d7a8c157c6ff9f98dd53a092d439c736cfe934 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 06:53:25 -0800 Subject: Simplify test --- test/scan-test.cc | 49 ++++++++++++++++++++++++++----------------------- test/scan.h | 1 + 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 993a4b3f..2f3cd6a2 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -68,40 +68,43 @@ TEST(scan_test, read_string_view) { EXPECT_EQ(s, "foo"); } +TEST(scan_test, separator) { + int n1 = 0, n2 = 0; + fmt::scan("10 20", "{} {}", n1, n2); + EXPECT_EQ(n1, 10); + EXPECT_EQ(n2, 20); +} + #ifdef FMT_HAVE_STRPTIME +struct num { + int value; +}; + namespace fmt { -template <> struct scanner { - std::string format; +template <> struct scanner { + bool hex = false; auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') ++it; - auto end = it; - while (end != ctx.end() && *end != '}') ++end; - format.reserve(detail::to_unsigned(end - it + 1)); - format.append(it, end); - format.push_back('\0'); - return end; + auto it = ctx.begin(), end = ctx.end(); + if (it != end && *it == 'x') hex = true; + if (it != end && *it != '}') throw_format_error("invalid format"); + return it; } template - auto scan(tm&, ScanContext& ctx) const -> typename ScanContext::iterator { - // TODO: replace strptime with get_time - // auto result = strptime(ctx.begin(), format.c_str(), &t); - // if (!result) throw format_error("failed to parse time"); - // return result; + auto scan(num&, ScanContext& ctx) const -> typename ScanContext::iterator { + // TODO + //return fmt::scan({ctx.begin(), ctx.end()}, "{}", n.value); return ctx.begin(); } }; } // namespace fmt TEST(scan_test, read_custom) { - /*auto input = "Date: 1985-10-25"; - auto t = tm(); - fmt::scan(input, "Date: {0:%Y-%m-%d}", t); - EXPECT_EQ(t.tm_year, 85); - EXPECT_EQ(t.tm_mon, 9); - EXPECT_EQ(t.tm_mday, 25);*/ + auto input = "42"; + auto n = num(); + fmt::scan(input, "{:}", n); + //EXPECT_EQ(n, 42); } #endif @@ -113,8 +116,8 @@ TEST(scan_test, invalid_format) { } TEST(scan_test, example) { - auto key = std::string(); - auto value = int(); + std::string key; + int value = 0; fmt::scan("answer = 42", "{} = {}", key, value); EXPECT_EQ(key, "answer"); EXPECT_EQ(value, 42); diff --git a/test/scan.h b/test/scan.h index c48357fc..2ea7bfc2 100644 --- a/test/scan.h +++ b/test/scan.h @@ -234,6 +234,7 @@ class file_scan_buffer : public scan_buffer { void consume() override { // Consume the current buffer content. + // TODO: do it more efficiently for (size_t i = 0, n = file_.buffer().size(); i != n; ++i) file_.get(); fill(); } -- cgit v1.2.3 From 3eb3aef5753b91d689b496a629b9dc5c4a7cd348 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 08:32:36 -0800 Subject: Fix handling of set_debug_format --- include/fmt/format.h | 7 ++----- test/ranges-test.cc | 13 +++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index dd04a79e..6caa4f9d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -4097,13 +4097,10 @@ class format_int { template struct formatter::value>> - : private formatter, Char> { - using base = formatter, Char>; - using base::parse; - using base::set_debug_format; - + : formatter, Char> { template auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { + using base = formatter, Char>; return base::format(format_as(value), ctx); } }; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index ba5c464d..c67eed9f 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -528,3 +528,16 @@ TEST(ranges_test, container_adaptor) { EXPECT_EQ(fmt::format("{}", m), "[1, 2]"); } } + +struct tieable { + int a = 3; + double b = 0.42; +}; + +auto format_as(const tieable& t) -> std::tuple { + return std::tie(t.a, t.b); +} + +TEST(ranges_test, format_as_tie) { + EXPECT_EQ(fmt::format("{}", tieable()), "(3, 0.42)"); +} -- cgit v1.2.3 From e7875ae0fa7634a89ebfe6086f88c1bd746bf77d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 09:11:50 -0800 Subject: Fix formatting of some nested ranges --- include/fmt/ranges.h | 3 ++- test/ranges-test.cc | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index d4501529..43f89d00 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -482,7 +482,8 @@ struct range_formatter< for (; it != end; ++it) { if (i > 0) out = detail::copy_str(separator_, out); ctx.advance_to(out); - out = underlying_.format(mapper.map(*it), ctx); + auto&& item = *it; + out = underlying_.format(mapper.map(item), ctx); ++i; } out = detail::copy_str(closing_bracket_, out); diff --git a/test/ranges-test.cc b/test/ranges-test.cc index c67eed9f..c161fbc4 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -7,13 +7,19 @@ #include "fmt/ranges.h" +#include #include +#include #include #include #include #include #include +#if FMT_HAS_INCLUDE() +# include +#endif + #include "gtest/gtest.h" #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601 @@ -242,7 +248,7 @@ template class non_const_only_range { explicit non_const_only_range(Args&&... args) : vec(std::forward(args)...) {} - auto begin() -> const_iterator{ return vec.begin(); } + auto begin() -> const_iterator { return vec.begin(); } auto end() -> const_iterator { return vec.end(); } }; @@ -360,7 +366,7 @@ struct cpp20_only_range { iterator() = default; iterator(int i) : val(i) {} auto operator*() const -> int { return val; } - auto operator++() -> iterator&{ + auto operator++() -> iterator& { ++val; return *this; } @@ -378,6 +384,17 @@ struct cpp20_only_range { }; static_assert(std::input_iterator); + +# ifdef __cpp_lib_ranges_iota +TEST(ranges_test, nested_ranges) { + auto l = std::list{1, 2, 3}; + auto r = std::views::iota(0, 3) | std::views::transform([&l](auto i) { + return std::views::take(std::ranges::subrange(l), i); + }) | + std::views::transform(std::views::reverse); + EXPECT_EQ(fmt::format("{}", r), "[[], [1], [2, 1]]"); +} +# endif # endif TEST(ranges_test, join_sentinel) { -- cgit v1.2.3 From 6f9a81678654bcc5d1e1299bf1666c20178bdb0f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 10:02:42 -0800 Subject: Enable test --- test/ranges-test.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/ranges-test.cc b/test/ranges-test.cc index c161fbc4..8ab66b33 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -384,17 +384,6 @@ struct cpp20_only_range { }; static_assert(std::input_iterator); - -# ifdef __cpp_lib_ranges_iota -TEST(ranges_test, nested_ranges) { - auto l = std::list{1, 2, 3}; - auto r = std::views::iota(0, 3) | std::views::transform([&l](auto i) { - return std::views::take(std::ranges::subrange(l), i); - }) | - std::views::transform(std::views::reverse); - EXPECT_EQ(fmt::format("{}", r), "[[], [1], [2, 1]]"); -} -# endif # endif TEST(ranges_test, join_sentinel) { @@ -433,6 +422,17 @@ TEST(ranges_test, join_range) { } #endif // FMT_RANGES_TEST_ENABLE_JOIN +#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202302L +TEST(ranges_test, nested_ranges) { + auto l = std::list{1, 2, 3}; + auto r = std::views::iota(0, 3) | std::views::transform([&l](auto i) { + return std::views::take(std::ranges::subrange(l), i); + }) | + std::views::transform(std::views::reverse); + EXPECT_EQ(fmt::format("{}", r), "[[], [1], [2, 1]]"); +} +#endif + TEST(ranges_test, is_printable) { using fmt::detail::is_printable; EXPECT_TRUE(is_printable(0x0323)); -- cgit v1.2.3 From f64a6a2ecd04915c275796a088ef7500a2bd7382 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 12:20:14 -0800 Subject: Update changelog --- ChangeLog.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index fc2eef43..33c2ba75 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,9 +1,27 @@ # 10.2.0 - TBD +- Added a formatter for `std::bitset` + (https://github.com/fmtlib/fmt/pull/3660). For example + ([godbolt](https://godbolt.org/z/bdEaGeYxe)): + + ```c++ + #include + #include + + int main() { + fmt::print("{}\n", std::bitset<6>(42)); // prints "101010" + } + ``` + + Thanks @muggenhor. + - Only export `format_error` when {fmt} is built as a shared library (https://github.com/fmtlib/fmt/issues/3626, https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. +- Made `fmt::streamed` `constexpr`. + (https://github.com/fmtlib/fmt/pull/3650). Thanks @muggenhor. + - Added an option to build without `wchar_t` support on Windows (https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. @@ -11,8 +29,20 @@ (https://github.com/fmtlib/fmt/pull/3610, https://github.com/fmtlib/fmt/pull/3624, https://github.com/fmtlib/fmt/pull/3630, - https://github.com/fmtlib/fmt/pull/3634). - Thanks @danakj, @vinayyadav3016, @cyyever, @phprus. + https://github.com/fmtlib/fmt/pull/3634, + https://github.com/fmtlib/fmt/pull/3638, + https://github.com/fmtlib/fmt/issues/3645, + https://github.com/fmtlib/fmt/pull/3647, + https://github.com/fmtlib/fmt/pull/3652). + Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, + @gsjaardema. + +- Improved documentation and README + (https://github.com/fmtlib/fmt/pull/3642, + https://github.com/fmtlib/fmt/pull/3653, + https://github.com/fmtlib/fmt/pull/3655, + https://github.com/fmtlib/fmt/pull/3661). + Thanks @idzm, @perlun, @joycebrum. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3615, -- cgit v1.2.3 From 7c240d52c3503864349b0588caf688586088c2c3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 13:20:55 -0800 Subject: Remove unused symbol --- include/fmt/format.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 6caa4f9d..d5010a40 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -278,12 +278,6 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE -template struct disjunction : std::false_type {}; -template struct disjunction

: P {}; -template -struct disjunction - : conditional_t> {}; - template struct conjunction : std::true_type {}; template struct conjunction

: P {}; template -- cgit v1.2.3 From a537c39fdffd5c225b855e2c75f996033ae37a80 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 14:35:11 -0800 Subject: Move conjunction to where it is used --- include/fmt/format.h | 7 ------- include/fmt/ranges.h | 6 ++++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index d5010a40..15045b9e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -277,13 +277,6 @@ FMT_END_NAMESPACE #endif FMT_BEGIN_NAMESPACE - -template struct conjunction : std::true_type {}; -template struct conjunction

: P {}; -template -struct conjunction - : conditional_t, P1> {}; - namespace detail { FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) { diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 43f89d00..3638fffb 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -417,6 +417,12 @@ struct is_formattable_delayed #endif } // namespace detail +template struct conjunction : std::true_type {}; +template struct conjunction

: P {}; +template +struct conjunction + : conditional_t, P1> {}; + template struct range_formatter; -- cgit v1.2.3 From 86f2ec5de755e65b12b453bff396488cffc48477 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 14:51:43 -0800 Subject: Fix a warning --- include/fmt/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 15045b9e..d7193146 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -43,7 +43,7 @@ #include // std::system_error #ifdef __cpp_lib_bit_cast -# include // std::bitcast +# include // std::bit_cast #endif #include "core.h" -- cgit v1.2.3 From fc0f84d29003bf4d13dfcc86c87f198d88509ed9 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 15:00:28 -0800 Subject: Move formatbuf to ostream.h --- include/fmt/chrono.h | 2 +- include/fmt/format.h | 31 ------------------------------- include/fmt/ostream.h | 32 +++++++++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 1a4b7d5a..6a3d38ff 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -18,7 +18,7 @@ #include #include -#include "format.h" +#include "ostream.h" // formatbuf FMT_BEGIN_NAMESPACE diff --git a/include/fmt/format.h b/include/fmt/format.h index d7193146..ded5d513 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -298,37 +298,6 @@ template constexpr CharT string_literal::value[sizeof...(C)]; #endif -template class formatbuf : public Streambuf { - private: - using char_type = typename Streambuf::char_type; - using streamsize = decltype(std::declval().sputn(nullptr, 0)); - using int_type = typename Streambuf::int_type; - using traits_type = typename Streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const char_type* s, streamsize count) -> streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - // Implementation of std::bit_cast for pre-C++20. template FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 3eb62143..26fb3b5a 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -21,9 +21,39 @@ #include "format.h" FMT_BEGIN_NAMESPACE - namespace detail { +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + // Generate a unique explicit instantion in every translation unit using a tag // type in an anonymous namespace. namespace { -- cgit v1.2.3 From d06921d8d8ee205b2baa97d97e99de754b6ad2fa Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 23 Dec 2023 15:30:26 -0800 Subject: Update changelog --- ChangeLog.md | 44 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 33c2ba75..46a1bc09 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,8 +1,23 @@ # 10.2.0 - TBD +- Added support for the chrono suffix for days and changed + the suffix for minutes from "m" to the correct "min" + (https://github.com/fmtlib/fmt/pull/3664). + For example ([godbolt](https://godbolt.org/z/9KhMnq9ba)): + + ```c++ + #include + + int main() { + fmt::print("{}\n", std::chrono::days(42)); // prints "42d" + } + ``` + + Thanks @Richardk2n. + - Added a formatter for `std::bitset` - (https://github.com/fmtlib/fmt/pull/3660). For example - ([godbolt](https://godbolt.org/z/bdEaGeYxe)): + (https://github.com/fmtlib/fmt/pull/3660). + For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)): ```c++ #include @@ -15,6 +30,13 @@ Thanks @muggenhor. +- Added synchronization with the underlying output stream when writing to + the Windows console + (https://github.com/fmtlib/fmt/pull/3668, + https://github.com/fmtlib/fmt/issues/3688, + https://github.com/fmtlib/fmt/pull/3689). + Thanks @Roman-Koshelev and @dimztimz. + - Only export `format_error` when {fmt} is built as a shared library (https://github.com/fmtlib/fmt/issues/3626, https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. @@ -33,21 +55,29 @@ https://github.com/fmtlib/fmt/pull/3638, https://github.com/fmtlib/fmt/issues/3645, https://github.com/fmtlib/fmt/pull/3647, - https://github.com/fmtlib/fmt/pull/3652). + https://github.com/fmtlib/fmt/pull/3652, + https://github.com/fmtlib/fmt/issues/3654, + https://github.com/fmtlib/fmt/pull/3663, + https://github.com/fmtlib/fmt/pull/3680, + https://github.com/fmtlib/fmt/pull/3695). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, - @gsjaardema. + @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad. - Improved documentation and README (https://github.com/fmtlib/fmt/pull/3642, https://github.com/fmtlib/fmt/pull/3653, https://github.com/fmtlib/fmt/pull/3655, - https://github.com/fmtlib/fmt/pull/3661). - Thanks @idzm, @perlun, @joycebrum. + https://github.com/fmtlib/fmt/pull/3661, + https://github.com/fmtlib/fmt/pull/3677). + Thanks @idzm, @perlun, @joycebrum, @fennewald. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3615, https://github.com/fmtlib/fmt/pull/3622, - https://github.com/fmtlib/fmt/pull/3623). + https://github.com/fmtlib/fmt/pull/3623, + https://github.com/fmtlib/fmt/pull/3666, + https://github.com/fmtlib/fmt/pull/3696, + https://github.com/fmtlib/fmt/pull/3697). # 10.1.1 - 2023-08-28 -- cgit v1.2.3 From c4f2de4933ae014cf81d5c4e350ca2eb25c67916 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 24 Dec 2023 07:32:27 -0800 Subject: Improve scan --- test/scan-test.cc | 12 ++++++++---- test/scan.h | 28 +++++++++++++++++----------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 2f3cd6a2..96ae3746 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -127,11 +127,15 @@ TEST(scan_test, example) { TEST(scan_test, file) { fmt::file read_end, write_end; fmt::file::pipe(read_end, write_end); - fmt::string_view input = "42"; + + fmt::string_view input = "10 20"; write_end.write(input.data(), input.size()); write_end.close(); - int value = 0; - fmt::scan(read_end.fdopen("r").get(), "{}", value); - EXPECT_EQ(value, 42); + + int n1 = 0, n2 = 0; + fmt::buffered_file f = read_end.fdopen("r"); + fmt::scan(f.get(), "{} {}", n1, n2); + EXPECT_EQ(n1, 10); + EXPECT_EQ(n2, 20); } #endif // FMT_USE_FCNTL diff --git a/test/scan.h b/test/scan.h index 2ea7bfc2..f7d3e1da 100644 --- a/test/scan.h +++ b/test/scan.h @@ -14,6 +14,10 @@ FMT_BEGIN_NAMESPACE namespace detail { +inline bool is_whitespace(char c) { + return c == ' ' || c == '\n'; +} + struct maybe_contiguous_range { const char* begin; const char* end; @@ -39,21 +43,19 @@ class scan_buffer { end_ = end; } + const char* ptr() const { return ptr_; } + auto peek() -> int { - if (ptr_ == end_) { - // TODO: refill buffer - return EOF; - } - return *ptr_; + return ptr_ != end_ ? *ptr_ : EOF; } - // Fills the buffer with more input if available. - virtual void consume() = 0; - public: scan_buffer(const scan_buffer&) = delete; void operator=(const scan_buffer&) = delete; + // Fills the buffer with more input if available. + virtual void consume() = 0; + class iterator { private: const char** ptr_; @@ -76,6 +78,7 @@ class scan_buffer { iterator(scan_buffer* buf) : ptr_(&buf->ptr_), buf_(buf), value_(static_cast(buf->peek())) { + // TODO: fix check if (value_ == EOF) ptr_ = sentinel(); } @@ -235,7 +238,8 @@ class file_scan_buffer : public scan_buffer { void consume() override { // Consume the current buffer content. // TODO: do it more efficiently - for (size_t i = 0, n = file_.buffer().size(); i != n; ++i) file_.get(); + size_t n = to_unsigned(ptr() - file_.buffer().begin()); + for (size_t i = 0; i != n; ++i) file_.get(); fill(); } @@ -284,8 +288,7 @@ struct scan_context { auto end() const -> iterator { return buf_.end(); } void advance_to(iterator) { - // The scan_buffer iterator automatically updates the buffer position when - // incremented. + buf_.consume(); } }; @@ -418,6 +421,7 @@ struct scan_handler : error_handler { auto pos() const -> scan_buffer::iterator { return scan_ctx_.begin(); } void on_text(const char* begin, const char* end) { + if (begin == end) return; auto it = scan_ctx_.begin(), scan_end = scan_ctx_.end(); for (; begin != end; ++begin, ++it) { if (it == scan_end || *begin != *it) on_error("invalid input"); @@ -438,6 +442,8 @@ struct scan_handler : error_handler { void on_replacement_field(int, const char*) { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + while (it != end && is_whitespace(*it)) ++it; + scan_ctx_.advance_to(it); switch (arg_.type) { case scan_type::int_type: *arg_.int_value = read_int(); -- cgit v1.2.3 From 1fd093add44aef46c61453dcc25366aeaaa5de9c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 06:41:37 -0800 Subject: Update changelog --- ChangeLog.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 46a1bc09..fbe46473 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -47,6 +47,10 @@ - Added an option to build without `wchar_t` support on Windows (https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. +- Improved build configuration + (https://github.com/fmtlib/fmt/issues/3701, + https://github.com/fmtlib/fmt/pull/3702). Thanks @pklima. + - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/3610, https://github.com/fmtlib/fmt/pull/3624, @@ -59,9 +63,14 @@ https://github.com/fmtlib/fmt/issues/3654, https://github.com/fmtlib/fmt/pull/3663, https://github.com/fmtlib/fmt/pull/3680, - https://github.com/fmtlib/fmt/pull/3695). + https://github.com/fmtlib/fmt/pull/3695, + https://github.com/fmtlib/fmt/pull/369, + https://github.com/fmtlib/fmt/issues/3712, + https://github.com/fmtlib/fmt/pull/3713, + https://github.com/fmtlib/fmt/pull/3716). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, - @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad. + @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, + @cptFracassa, @kuguma. - Improved documentation and README (https://github.com/fmtlib/fmt/pull/3642, -- cgit v1.2.3 From c5a85f8d7d9bef3e1befb2232fff92df32d67145 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 07:18:23 -0800 Subject: Handle end of input in scan --- test/scan-test.cc | 6 ++++++ test/scan.h | 47 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 96ae3746..5d6585ef 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -10,6 +10,7 @@ #include #include +#include #include "fmt/os.h" #include "gmock/gmock.h" @@ -123,6 +124,11 @@ TEST(scan_test, example) { EXPECT_EQ(value, 42); } +TEST(scan_test, end_of_input) { + int value = 0; + fmt::scan("", "{}", value); +} + #if FMT_USE_FCNTL TEST(scan_test, file) { fmt::file read_end, write_end; diff --git a/test/scan.h b/test/scan.h index f7d3e1da..3032b7bb 100644 --- a/test/scan.h +++ b/test/scan.h @@ -18,6 +18,26 @@ inline bool is_whitespace(char c) { return c == ' ' || c == '\n'; } +template +class optional { + private: + T value_; + bool has_value_ = false; + + public: + optional() = default; + optional(T value) : value_(std::move(value)), has_value_(true) {} + + explicit operator bool() const { + return has_value_; + } + + const T& operator*() const { + if (!has_value_) throw std::runtime_error("bad optional access"); + return value_; + } +}; + struct maybe_contiguous_range { const char* begin; const char* end; @@ -374,13 +394,15 @@ struct scan_handler : error_handler { int next_arg_id_; scan_arg arg_; - template auto read_uint() -> T { + template auto read_uint() -> optional { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); - char c = it != end ? *it : '\0', prev_digit; + if (it == end) return {}; + char c = *it; if (c < '0' || c > '9') on_error("invalid input"); int num_digits = 0; T value = 0, prev = 0; + char prev_digit = c; do { prev = value; value = value * 10 + static_cast(c - '0'); @@ -401,16 +423,19 @@ struct scan_handler : error_handler { throw format_error("number is too big"); } - template auto read_int() -> T { + template auto read_int() -> optional { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); bool negative = it != end && *it == '-'; if (negative) { ++it; scan_ctx_.advance_to(it); } - auto abs_value = read_uint::type>(); - auto value = static_cast(abs_value); - return negative ? -value : value; + if (auto abs_value = read_uint::type>()) { + auto value = static_cast(*abs_value); + return negative ? -value : value; + } + if (negative) on_error("invalid input"); + return {}; } public: @@ -446,16 +471,18 @@ struct scan_handler : error_handler { scan_ctx_.advance_to(it); switch (arg_.type) { case scan_type::int_type: - *arg_.int_value = read_int(); + if (auto value = read_int()) *arg_.int_value = *value; + // TODO: stop on end of input break; case scan_type::uint_type: - *arg_.uint_value = read_uint(); + if (auto value = read_uint()) *arg_.uint_value = *value; break; case scan_type::long_long_type: - *arg_.long_long_value = read_int(); + if (auto value = read_int()) *arg_.long_long_value = *value; break; case scan_type::ulong_long_type: - *arg_.ulong_long_value = read_uint(); + if (auto value = read_uint()) + *arg_.ulong_long_value = *value; break; case scan_type::string_type: while (it != end && *it != ' ') arg_.string->push_back(*it++); -- cgit v1.2.3 From e450b7aeb3f4790627cd0d3d717b74037dbee3ff Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 08:39:14 -0800 Subject: Implement locking --- test/scan-test.cc | 29 +++++++++++++++++++++++++++++ test/scan.h | 22 +++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 5d6585ef..e8fd75e5 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -144,4 +144,33 @@ TEST(scan_test, file) { EXPECT_EQ(n1, 10); EXPECT_EQ(n2, 20); } + +TEST(scan_test, lock) { + fmt::file read_end, write_end; + fmt::file::pipe(read_end, write_end); + + std::thread producer([&]() { + fmt::string_view input = "42 "; + for (int i = 0; i < 1000; ++i) + write_end.write(input.data(), input.size()); + write_end.close(); + }); + + fmt::buffered_file f = read_end.fdopen("r"); + auto fun = [&]() { + int value = 0; + while (fmt::scan(f.get(), "{}", value)) { + if (value != 42) { + read_end.close(); + EXPECT_EQ(value, 42); + break; + } + } + }; + std::thread consumer1(fun); + std::thread consumer2(fun); + producer.join(); + consumer1.join(); + consumer2.join(); +} #endif // FMT_USE_FCNTL diff --git a/test/scan.h b/test/scan.h index 3032b7bb..ea7c51ba 100644 --- a/test/scan.h +++ b/test/scan.h @@ -266,9 +266,20 @@ class file_scan_buffer : public scan_buffer { public: explicit file_scan_buffer(FILE* f) : scan_buffer(nullptr, nullptr, false), file_(f) { - // TODO: lock file? +#ifndef _WIN32 + flockfile(f); +#else + _lock_file(f); +#endif fill(); } + ~file_scan_buffer() { +#ifndef _WIN32 + funlockfile(file_); +#else + _unlock_file(file_); +#endif + } }; } // namespace detail @@ -472,7 +483,6 @@ struct scan_handler : error_handler { switch (arg_.type) { case scan_type::int_type: if (auto value = read_int()) *arg_.int_value = *value; - // TODO: stop on end of input break; case scan_type::uint_type: if (auto value = read_uint()) *arg_.uint_value = *value; @@ -512,6 +522,11 @@ struct scan_handler : error_handler { arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); return parse_ctx_.begin(); } + + void on_error(const char* message) { + scan_ctx_.advance_to(scan_ctx_.end()); + error_handler::on_error(message); + } }; } // namespace detail @@ -533,9 +548,10 @@ auto scan(string_view input, string_view fmt, T&... args) return input.begin() + (buf.begin().base() - input.data()); } -template void scan(std::FILE* f, string_view fmt, T&... args) { +template bool scan(std::FILE* f, string_view fmt, T&... args) { auto&& buf = detail::file_scan_buffer(f); vscan(buf, fmt, make_scan_args(args...)); + return buf.begin() != buf.end(); } FMT_END_NAMESPACE -- cgit v1.2.3 From 0a9d08fefd23e861b7675dc4808fad885a9a45a4 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 09:00:03 -0800 Subject: Simplify fallback --- test/scan.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/scan.h b/test/scan.h index ea7c51ba..c5dcb772 100644 --- a/test/scan.h +++ b/test/scan.h @@ -244,6 +244,15 @@ class file_scan_buffer : public scan_buffer { private: decltype(get_file(static_cast(nullptr), 0)) file_; +#ifdef _WIN32 + static void flockfile(FILE* f) { + _lock_file(f); + } + static void funlockfile(FILE* f) { + _unlock_file(file_); + } +#endif + void fill() { string_view buf = file_.buffer(); if (buf.size() == 0) { @@ -266,19 +275,11 @@ class file_scan_buffer : public scan_buffer { public: explicit file_scan_buffer(FILE* f) : scan_buffer(nullptr, nullptr, false), file_(f) { -#ifndef _WIN32 flockfile(f); -#else - _lock_file(f); -#endif fill(); } ~file_scan_buffer() { -#ifndef _WIN32 funlockfile(file_); -#else - _unlock_file(file_); -#endif } }; } // namespace detail -- cgit v1.2.3 From 41c24333584a9ef9040598e024c5ebd76791677e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 09:05:26 -0800 Subject: clang-format --- test/scan-test.cc | 7 +++---- test/scan.h | 35 ++++++++++------------------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index e8fd75e5..f9ea9379 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -95,7 +95,7 @@ template <> struct scanner { template auto scan(num&, ScanContext& ctx) const -> typename ScanContext::iterator { // TODO - //return fmt::scan({ctx.begin(), ctx.end()}, "{}", n.value); + // return fmt::scan({ctx.begin(), ctx.end()}, "{}", n.value); return ctx.begin(); } }; @@ -105,7 +105,7 @@ TEST(scan_test, read_custom) { auto input = "42"; auto n = num(); fmt::scan(input, "{:}", n); - //EXPECT_EQ(n, 42); + // EXPECT_EQ(n, 42); } #endif @@ -151,8 +151,7 @@ TEST(scan_test, lock) { std::thread producer([&]() { fmt::string_view input = "42 "; - for (int i = 0; i < 1000; ++i) - write_end.write(input.data(), input.size()); + for (int i = 0; i < 1000; ++i) write_end.write(input.data(), input.size()); write_end.close(); }); diff --git a/test/scan.h b/test/scan.h index c5dcb772..f81709a5 100644 --- a/test/scan.h +++ b/test/scan.h @@ -14,12 +14,9 @@ FMT_BEGIN_NAMESPACE namespace detail { -inline bool is_whitespace(char c) { - return c == ' ' || c == '\n'; -} +inline bool is_whitespace(char c) { return c == ' ' || c == '\n'; } -template -class optional { +template class optional { private: T value_; bool has_value_ = false; @@ -28,9 +25,7 @@ class optional { optional() = default; optional(T value) : value_(std::move(value)), has_value_(true) {} - explicit operator bool() const { - return has_value_; - } + explicit operator bool() const { return has_value_; } const T& operator*() const { if (!has_value_) throw std::runtime_error("bad optional access"); @@ -65,9 +60,7 @@ class scan_buffer { const char* ptr() const { return ptr_; } - auto peek() -> int { - return ptr_ != end_ ? *ptr_ : EOF; - } + auto peek() -> int { return ptr_ != end_ ? *ptr_ : EOF; } public: scan_buffer(const scan_buffer&) = delete; @@ -245,14 +238,11 @@ class file_scan_buffer : public scan_buffer { decltype(get_file(static_cast(nullptr), 0)) file_; #ifdef _WIN32 - static void flockfile(FILE* f) { - _lock_file(f); - } - static void funlockfile(FILE* f) { - _unlock_file(file_); - } + static void flockfile(FILE* f) { _lock_file(f); } + static void funlockfile(FILE* f) { _unlock_file(f); } #endif + // Fills the buffer if it is empty. void fill() { string_view buf = file_.buffer(); if (buf.size() == 0) { @@ -266,7 +256,6 @@ class file_scan_buffer : public scan_buffer { void consume() override { // Consume the current buffer content. - // TODO: do it more efficiently size_t n = to_unsigned(ptr() - file_.buffer().begin()); for (size_t i = 0; i != n; ++i) file_.get(); fill(); @@ -278,9 +267,7 @@ class file_scan_buffer : public scan_buffer { flockfile(f); fill(); } - ~file_scan_buffer() { - funlockfile(file_); - } + ~file_scan_buffer() { funlockfile(file_); } }; } // namespace detail @@ -319,9 +306,7 @@ struct scan_context { auto begin() const -> iterator { return buf_.begin(); } auto end() const -> iterator { return buf_.end(); } - void advance_to(iterator) { - buf_.consume(); - } + void advance_to(iterator) { buf_.consume(); } }; namespace detail { @@ -424,7 +409,7 @@ struct scan_handler : error_handler { if (c < '0' || c > '9') break; } while (it != end); scan_ctx_.advance_to(it); - + // Check overflow. if (num_digits <= std::numeric_limits::digits10) return value; const unsigned max = to_unsigned((std::numeric_limits::max)()); -- cgit v1.2.3 From eef6dbafbf2953b4aaf004edbce6a9fe84c9c52a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 09:22:29 -0800 Subject: Refactor file layer in scan --- test/scan.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/test/scan.h b/test/scan.h index f81709a5..9b6f5d6b 100644 --- a/test/scan.h +++ b/test/scan.h @@ -152,6 +152,12 @@ class string_scan_buffer : public scan_buffer { : scan_buffer(s.begin(), s.end(), true) {} }; +#ifdef _WIN32 +void flockfile(FILE* f) { _lock_file(f); } +void funlockfile(FILE* f) { _unlock_file(f); } +int getc_unlocked(FILE *f) { return _fgetc_nolock(f); } +#endif + // A FILE wrapper. F is FILE defined as a template parameter to make // system-specific API detection work. template class file_base { @@ -164,7 +170,7 @@ template class file_base { // Reads a code unit from the stream. auto get() -> int { - int result = getc(file_); + int result = getc_unlocked(file_); if (result == EOF && ferror(file_) != 0) FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); return result; @@ -182,6 +188,7 @@ template class glibc_file : public file_base { public: using file_base::file_base; + // Returns the file's read buffer as a string_view. auto buffer() const -> string_view { return {this->file_->_IO_read_ptr, to_unsigned(this->file_->_IO_read_end - this->file_->_IO_read_ptr)}; @@ -193,7 +200,6 @@ template class apple_file : public file_base { public: using file_base::file_base; - // Returns the file's read buffer as a string_view. auto buffer() const -> string_view { return {reinterpret_cast(this->file_->_p), to_unsigned(this->file_->_r)}; @@ -223,24 +229,19 @@ template class fallback_file : public file_base { } }; -template -auto get_file(F* file, int) -> glibc_file { - return file; -} -template -auto get_file(F* file, int) -> apple_file { - return file; -} -auto get_file(FILE* file, ...) -> fallback_file { return file; } - class file_scan_buffer : public scan_buffer { private: - decltype(get_file(static_cast(nullptr), 0)) file_; + template + static auto get_file(F* f, int) -> glibc_file { + return f; + } + template + static auto get_file(F* f, int) -> apple_file { + return f; + } + static auto get_file(FILE* f, ...) -> fallback_file { return f; } -#ifdef _WIN32 - static void flockfile(FILE* f) { _lock_file(f); } - static void funlockfile(FILE* f) { _unlock_file(f); } -#endif + decltype(get_file(static_cast(nullptr), 0)) file_; // Fills the buffer if it is empty. void fill() { -- cgit v1.2.3 From df62c8678376377e9d5027e206c7d3f40d047196 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 10:11:22 -0800 Subject: Mark grow as deprecated --- include/fmt/core.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/fmt/core.h b/include/fmt/core.h index ce9f0e70..1e0503db 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -836,6 +836,7 @@ template class buffer { } /** Increases the buffer capacity to hold at least *capacity* elements. */ + // DEPRECATED! virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; public: -- cgit v1.2.3 From 62529aad1982dbc47551619f472a9ffb4dff3e4c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 10:16:55 -0800 Subject: Apply coding conventions --- test/scan.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/scan.h b/test/scan.h index 9b6f5d6b..9ea3f48e 100644 --- a/test/scan.h +++ b/test/scan.h @@ -27,7 +27,7 @@ template class optional { explicit operator bool() const { return has_value_; } - const T& operator*() const { + auto operator*() const -> const T& { if (!has_value_) throw std::runtime_error("bad optional access"); return value_; } @@ -58,7 +58,7 @@ class scan_buffer { end_ = end; } - const char* ptr() const { return ptr_; } + auto ptr() const -> const char* { return ptr_; } auto peek() -> int { return ptr_ != end_ ? *ptr_ : EOF; } @@ -252,7 +252,7 @@ class file_scan_buffer : public scan_buffer { if (c != EOF) file_.unget(static_cast(c)); buf = file_.buffer(); } - this->set(buf.begin(), buf.end()); + set(buf.begin(), buf.end()); } void consume() override { -- cgit v1.2.3 From 88d19f5de979ec0a91b642328a11b54c19601847 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 10:28:50 -0800 Subject: Cleanup scan --- test/scan.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/scan.h b/test/scan.h index 9ea3f48e..212d1844 100644 --- a/test/scan.h +++ b/test/scan.h @@ -51,11 +51,9 @@ class scan_buffer { : ptr_(ptr), end_(end), contiguous_(contiguous) {} ~scan_buffer() = default; - auto is_empty() const -> bool { return ptr_ == end_; } - - void set(const char* ptr, const char* end) noexcept { - ptr_ = ptr; - end_ = end; + void set(string_view buf) { + ptr_ = buf.begin(); + end_ = buf.end(); } auto ptr() const -> const char* { return ptr_; } @@ -128,12 +126,12 @@ class scan_buffer { if (ptr == it.buf_->end_) it.ptr_ = iterator::sentinel(); } - auto begin() noexcept -> iterator { return this; } - auto end() noexcept -> iterator { return {}; } + auto begin() -> iterator { return this; } + auto end() -> iterator { return {}; } auto is_contiguous() const -> bool { return contiguous_; } - // Tries consuming a single code unit. + // Tries consuming a single code unit. Returns true iff there is more input. auto try_consume() -> bool { FMT_ASSERT(ptr_ != end_, ""); ++ptr_; @@ -252,7 +250,7 @@ class file_scan_buffer : public scan_buffer { if (c != EOF) file_.unget(static_cast(c)); buf = file_.buffer(); } - set(buf.begin(), buf.end()); + set(buf); } void consume() override { -- cgit v1.2.3 From 4cbf6182eab53a7d8f48646e1ba8f015d38ebbd9 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 25 Dec 2023 10:47:45 -0800 Subject: Remove peek --- test/scan.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/scan.h b/test/scan.h index 212d1844..c03356a9 100644 --- a/test/scan.h +++ b/test/scan.h @@ -58,8 +58,6 @@ class scan_buffer { auto ptr() const -> const char* { return ptr_; } - auto peek() -> int { return ptr_ != end_ ? *ptr_ : EOF; } - public: scan_buffer(const scan_buffer&) = delete; void operator=(const scan_buffer&) = delete; @@ -87,10 +85,13 @@ class scan_buffer { return *lhs.ptr_ != *rhs.ptr_; } - iterator(scan_buffer* buf) - : ptr_(&buf->ptr_), buf_(buf), value_(static_cast(buf->peek())) { - // TODO: fix check - if (value_ == EOF) ptr_ = sentinel(); + iterator(scan_buffer* buf) : buf_(buf) { + if (buf->ptr_ == buf->end_) { + ptr_ = sentinel(); + return; + } + ptr_ = &buf->ptr_; + value_ = *buf->ptr_; } public: -- cgit v1.2.3 From 3a25a5848258aa2527d45f404257da1c488694fd Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 26 Dec 2023 11:12:02 -0800 Subject: Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 697a6efa..dcfb16ec 100644 --- a/README.md +++ b/README.md @@ -96,22 +96,22 @@ std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy"); // s == "I'd rather be happy than right." ``` -**Print chrono durations** ([run](https://godbolt.org/z/K8s4Mc)) +**Print dates and times** ([run](https://godbolt.org/z/c31ExdY3W)) ``` c++ #include int main() { - using namespace std::literals::chrono_literals; - fmt::print("Default format: {} {}\n", 42s, 100ms); - fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s); + auto now = std::chrono::system_clock::now(); + fmt::print("Date and time: {}\n", now); + fmt::print("Time: {:%H:%M}\n", now); } ``` Output: - Default format: 42s 100ms - strftime-like format: 03:15:30 + Date and time: 2023-12-26 19:10:31.557195597 + Time: 19:10 **Print a container** ([run](https://godbolt.org/z/MxM1YqjE7)) -- cgit v1.2.3 From 18ca2248df204aeebc4d52fa3eca64a005f4405a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 06:39:00 -0800 Subject: Document '?' --- doc/syntax.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index 74b64c5a..53385d87 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -82,7 +82,7 @@ The general form of a *standard format specifier* is: width: `integer` | "{" [`arg_id`] "}" precision: `integer` | "{" [`arg_id`] "}" type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | - : "o" | "p" | "s" | "x" | "X" + : "o" | "p" | "s" | "x" | "X" | "?" The *fill* character can be any Unicode code point other than ``'{'`` or ``'}'``. The presence of a fill character is signaled by the character following @@ -177,6 +177,9 @@ The available string presentation types are: | ``'s'`` | String format. This is the default type for strings and | | | may be omitted. | +---------+----------------------------------------------------------+ +| ``'?'`` | Debug format. The string is quoted and special | +| | characters escaped. | ++---------+----------------------------------------------------------+ | none | The same as ``'s'``. | +---------+----------------------------------------------------------+ @@ -188,6 +191,9 @@ The available character presentation types are: | ``'c'`` | Character format. This is the default type for | | | characters and may be omitted. | +---------+----------------------------------------------------------+ +| ``'?'`` | Debug format. The character is quoted and special | +| | characters escaped. | ++---------+----------------------------------------------------------+ | none | The same as ``'c'``. | +---------+----------------------------------------------------------+ -- cgit v1.2.3 From 76e8f10403f3aa7896b1b1209b22731da34369c3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 07:23:56 -0800 Subject: Update changelog --- ChangeLog.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index fbe46473..27a7cf9d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -15,6 +15,10 @@ Thanks @Richardk2n. +- Fixed an overflow in `std::chrono::time_point` formatting with large dates + (https://github.com/fmtlib/fmt/issues/3725, + https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib. + - Added a formatter for `std::bitset` (https://github.com/fmtlib/fmt/pull/3660). For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)): @@ -37,8 +41,8 @@ https://github.com/fmtlib/fmt/pull/3689). Thanks @Roman-Koshelev and @dimztimz. -- Only export `format_error` when {fmt} is built as a shared library - (https://github.com/fmtlib/fmt/issues/3626, +- Changed to only export `format_error` when {fmt} is built as a shared + library (https://github.com/fmtlib/fmt/issues/3626, https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus. - Made `fmt::streamed` `constexpr`. @@ -67,16 +71,18 @@ https://github.com/fmtlib/fmt/pull/369, https://github.com/fmtlib/fmt/issues/3712, https://github.com/fmtlib/fmt/pull/3713, - https://github.com/fmtlib/fmt/pull/3716). + https://github.com/fmtlib/fmt/pull/3716, + https://github.com/fmtlib/fmt/pull/3723). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, - @cptFracassa, @kuguma. + @cptFracassa, @kuguma, @PeterJohnson . - Improved documentation and README (https://github.com/fmtlib/fmt/pull/3642, https://github.com/fmtlib/fmt/pull/3653, https://github.com/fmtlib/fmt/pull/3655, https://github.com/fmtlib/fmt/pull/3661, + https://github.com/fmtlib/fmt/issues/3673, https://github.com/fmtlib/fmt/pull/3677). Thanks @idzm, @perlun, @joycebrum, @fennewald. -- cgit v1.2.3 From 47c8f63d02e233d9bc736866326fc28038f745d3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 07:27:33 -0800 Subject: Remove redundant specifier --- include/fmt/std.h | 8 ++++---- test/std-test.cc | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/fmt/std.h b/include/fmt/std.h index a9a9dc33..7cff1159 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -120,7 +120,7 @@ template struct formatter { format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; - char path_type_ = 'n'; + char path_type_ = 0; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } @@ -137,7 +137,7 @@ template struct formatter { debug_ = true; ++it; } - if (it != end && (*it == 'g' || *it == 'n')) path_type_ = *it++; + if (it != end && (*it == 'g')) path_type_ = *it++; return it; } @@ -145,9 +145,9 @@ template struct formatter { auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; # ifdef _WIN32 - auto path_string = path_type_ == 'n' ? p.native() : p.generic_wstring(); + auto path_string = !path_type_ ? p.native() : p.generic_wstring(); # else - auto path_string = path_type_ == 'n' ? p.native() : p.generic_string(); + auto path_string = !path_type_ ? p.native() : p.generic_string(); # endif detail::handle_dynamic_spec(specs.width, width_ref_, diff --git a/test/std-test.cc b/test/std-test.cc index f288e144..de3feaa0 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -26,10 +26,9 @@ TEST(std_test, path) { EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar"); EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\""); - EXPECT_EQ(fmt::format("{:n}", path("/usr/bin")), "/usr/bin"); EXPECT_EQ(fmt::format("{:g}", path("/usr/bin")), "/usr/bin"); # ifdef _WIN32 - EXPECT_EQ(fmt::format("{:n}", path("C:\\foo")), "C:\\foo"); + EXPECT_EQ(fmt::format("{}", path("C:\\foo")), "C:\\foo"); EXPECT_EQ(fmt::format("{:g}", path("C:\\foo")), "C:/foo"); EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448" -- cgit v1.2.3 From 305747d4402a2cd48f48477ee06c171e7a70f286 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 08:01:56 -0800 Subject: Update changelog --- ChangeLog.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 27a7cf9d..2a0fad32 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,9 @@ # 10.2.0 - TBD +- Added support for the `%j` specifier (the number of days) for + `std::chrono::duration` (https://github.com/fmtlib/fmt/issues/3643, + https://github.com/fmtlib/fmt/pull/3732). Thanks @intelfx. + - Added support for the chrono suffix for days and changed the suffix for minutes from "m" to the correct "min" (https://github.com/fmtlib/fmt/pull/3664). @@ -19,6 +23,26 @@ (https://github.com/fmtlib/fmt/issues/3725, https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib. +- Added a formatter for `std::source_location` + (https://github.com/fmtlib/fmt/pull/3730). For example + ([godbolt](https://godbolt.org/z/YajfKjhhr)): + + ```c++ + #include + + int main() { + fmt::print("{}\n", std::source_location::current()); + } + ``` + + prints + + ``` + /app/example.cpp:5:51: int main() + ``` + + Thanks @felix642. + - Added a formatter for `std::bitset` (https://github.com/fmtlib/fmt/pull/3660). For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)): @@ -34,6 +58,23 @@ Thanks @muggenhor. +- Added the generic representation (`g`) to `std::filesystem::path` + (https://github.com/fmtlib/fmt/issues/3715, + https://github.com/fmtlib/fmt/pull/3729). For example: + + ```c++ + #include + #include + + int main() { + fmt::print("{:g}\n", std::filesystem::path("C:\\foo")); + } + ``` + + prints `"C:/foo"` on Windows. + + Thanks @js324. + - Added synchronization with the underlying output stream when writing to the Windows console (https://github.com/fmtlib/fmt/pull/3668, -- cgit v1.2.3 From be57ec7ec0c24a0fcfef774bf1e586ef10686070 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 16:14:32 -0800 Subject: Fix chrono-test on platforms with 32-bit time_t --- test/chrono-test.cc | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 56fbd382..fea296e8 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -17,6 +17,9 @@ using fmt::runtime; using testing::Contains; +template +using sys_time = std::chrono::time_point; + #if defined(__MINGW32__) && !defined(_UCRT) // Only C89 conversion specifiers when using MSVCRT instead of UCRT # define FMT_HAS_C99_STRFTIME 0 @@ -858,42 +861,33 @@ TEST(chrono_test, utc_clock) { } #endif -TEST(chrono_test, timestamps_ratios) { - std::chrono::time_point - t1(std::chrono::milliseconds(67890)); - +TEST(chrono_test, timestamp_ratios) { + auto t1 = sys_time(std::chrono::milliseconds(67890)); EXPECT_EQ(fmt::format("{:%M:%S}", t1), "01:07.890"); - std::chrono::time_point t2( - std::chrono::minutes(7)); - + auto t2 = sys_time(std::chrono::minutes(7)); EXPECT_EQ(fmt::format("{:%M:%S}", t2), "07:00"); - std::chrono::time_point>> - t3(std::chrono::duration>(7)); - + auto t3 = sys_time>>( + std::chrono::duration>(7)); EXPECT_EQ(fmt::format("{:%M:%S}", t3), "01:03"); - std::chrono::time_point>> - t4(std::chrono::duration>(1)); - + auto t4 = sys_time>>( + std::chrono::duration>(1)); EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03"); - std::chrono::time_point - t5(std::chrono::seconds(32503680000)); - - EXPECT_EQ(fmt::format("{:%Y-%m-%d}", t5), "3000-01-01"); - -#if FMT_SAFE_DURATION_CAST - using years = std::chrono::duration>; - std::chrono::time_point t6( - (years(std::numeric_limits::max()))); + if (sizeof(time_t) > 4) { + auto tp = sys_time( + std::chrono::seconds(32503680000)); + EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tp), "3000-01-01"); + } - EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", t6), fmt::format_error, - "cannot format duration"); -#endif + if (FMT_SAFE_DURATION_CAST) { + using years = std::chrono::duration>; + auto tp = sys_time(years(std::numeric_limits::max())); + EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", tp), fmt::format_error, + "cannot format duration"); + } } TEST(chrono_test, timestamps_sub_seconds) { -- cgit v1.2.3 From ea1066bbe3f8b78bdce623a7d8d7312ea82968f7 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 28 Dec 2023 16:56:42 -0800 Subject: Cleanup test --- test/chrono-test.cc | 118 +++++++++++++++++++++------------------------------- 1 file changed, 47 insertions(+), 71 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index fea296e8..3ff1c5b5 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -269,9 +269,8 @@ TEST(chrono_test, system_clock_time_point) { EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1)); EXPECT_EQ(strftime_full_utc(t1), fmt::format("{}", t1)); EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:}", t1)); - using time_point = - std::chrono::time_point; - auto t2 = time_point(std::chrono::seconds(42)); + + auto t2 = sys_time(std::chrono::seconds(42)); EXPECT_EQ(strftime_full_utc(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2)); std::vector spec_list = { @@ -713,27 +712,26 @@ TEST(chrono_test, invalid_colons) { } TEST(chrono_test, negative_durations) { - EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345))); - EXPECT_EQ("-03:25:45", - fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345))); - EXPECT_EQ("-00:01", - fmt::format("{:%M:%S}", std::chrono::duration(-1))); - EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345))); - EXPECT_EQ("-00.127", - fmt::format("{:%S}", - std::chrono::duration{-127})); + EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(-12345)), "-12345"); + EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)), + "-03:25:45"); + EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration(-1)), + "-00:01"); + EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(-12345)), "s"); + EXPECT_EQ(fmt::format("{:%S}", + std::chrono::duration(-127)), + "-00.127"); auto min = std::numeric_limits::min(); EXPECT_EQ(fmt::format("{}", min), fmt::format("{:%Q}", std::chrono::duration(min))); } TEST(chrono_test, special_durations) { - auto value = fmt::format("{:%S}", std::chrono::duration(1e20)); - EXPECT_EQ(value, "40"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration(1e20)), "40"); auto nan = std::numeric_limits::quiet_NaN(); EXPECT_EQ( - "nan nan nan nan nan:nan nan", - fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration(nan))); + fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration(nan)), + "nan nan nan nan nan:nan nan"); EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), "1Es"); EXPECT_EQ(fmt::format("{}", std::chrono::duration(1)), @@ -742,13 +740,13 @@ TEST(chrono_test, special_durations) { "03:33"); EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration{2}), "03:33:20"); - EXPECT_EQ("01.234", - fmt::format("{:.3%S}", - std::chrono::duration(1.234e12))); + EXPECT_EQ( + fmt::format("{:.3%S}", std::chrono::duration(1.234e12)), + "01.234"); } TEST(chrono_test, unsigned_duration) { - EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration(42))); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), "42s"); } TEST(chrono_test, weekday) { @@ -862,7 +860,8 @@ TEST(chrono_test, utc_clock) { #endif TEST(chrono_test, timestamp_ratios) { - auto t1 = sys_time(std::chrono::milliseconds(67890)); + auto t1 = + sys_time(std::chrono::milliseconds(67890)); EXPECT_EQ(fmt::format("{:%M:%S}", t1), "01:07.890"); auto t2 = sys_time(std::chrono::minutes(7)); @@ -877,8 +876,8 @@ TEST(chrono_test, timestamp_ratios) { EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03"); if (sizeof(time_t) > 4) { - auto tp = sys_time( - std::chrono::seconds(32503680000)); + auto tp = + sys_time(std::chrono::seconds(32503680000)); EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tp), "3000-01-01"); } @@ -890,79 +889,56 @@ TEST(chrono_test, timestamp_ratios) { } } -TEST(chrono_test, timestamps_sub_seconds) { - std::chrono::time_point>> - t1(std::chrono::duration>(4)); - +TEST(chrono_test, timestamp_sub_seconds) { + auto t1 = sys_time>>( + std::chrono::duration>(4)); EXPECT_EQ(fmt::format("{:%S}", t1), "01.333333"); - std::chrono::time_point>> - t2(std::chrono::duration>(4)); - + auto t2 = sys_time>>( + std::chrono::duration>(4)); EXPECT_EQ(fmt::format("{:%S}", t2), "01.333333"); - const std::chrono::time_point - t3(std::chrono::seconds(2)); - + auto t3 = sys_time(std::chrono::seconds(2)); EXPECT_EQ(fmt::format("{:%S}", t3), "02"); - const std::chrono::time_point> - t4(std::chrono::duration>(9.5)); - + auto t4 = sys_time>( + std::chrono::duration>(9.5)); EXPECT_EQ(fmt::format("{:%S}", t4), "09.500000"); - const std::chrono::time_point> - t5(std::chrono::duration>(9)); - + auto t5 = sys_time>( + std::chrono::duration>(9)); EXPECT_EQ(fmt::format("{:%S}", t5), "09"); - const std::chrono::time_point - t6(std::chrono::seconds(1) + std::chrono::milliseconds(120)); - + auto t6 = sys_time(std::chrono::seconds(1) + + std::chrono::milliseconds(120)); EXPECT_EQ(fmt::format("{:%S}", t6), "01.120"); - const std::chrono::time_point - t7(std::chrono::microseconds(1234567)); - + auto t7 = + sys_time(std::chrono::microseconds(1234567)); EXPECT_EQ(fmt::format("{:%S}", t7), "01.234567"); - const std::chrono::time_point - t8(std::chrono::nanoseconds(123456789)); - + auto t8 = + sys_time(std::chrono::nanoseconds(123456789)); EXPECT_EQ(fmt::format("{:%S}", t8), "00.123456789"); - const auto t9 = std::chrono::time_point_cast( + auto t9 = std::chrono::time_point_cast( std::chrono::system_clock::now()); - const auto t9_sec = std::chrono::time_point_cast(t9); + auto t9_sec = std::chrono::time_point_cast(t9); auto t9_sub_sec_part = fmt::format("{0:09}", (t9 - t9_sec).count()); - EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part), fmt::format("{:%Y-%m-%d %H:%M:%S}", t9)); EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part), fmt::format("{:%Y-%m-%d %T}", t9)); - const std::chrono::time_point - t10(std::chrono::milliseconds(2000)); - + auto t10 = + sys_time(std::chrono::milliseconds(2000)); EXPECT_EQ(fmt::format("{:%S}", t10), "02.000"); - { - const auto epoch = std::chrono::time_point(); - const auto d = std::chrono::milliseconds(250); - - EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - d)); - EXPECT_EQ("00.000", fmt::format("{:%S}", epoch)); - EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + d)); - } + auto epoch = sys_time(); + auto d = std::chrono::milliseconds(250); + EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - d)); + EXPECT_EQ("00.000", fmt::format("{:%S}", epoch)); + EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + d)); } TEST(chrono_test, glibc_extensions) { -- cgit v1.2.3 From a3bf40838f6bf9b45544d3d3cd0f6a60e8c7218c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 06:25:57 -0800 Subject: Initial range support in scan --- test/scan-test.cc | 11 ++++------- test/scan.h | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index f9ea9379..e2bfea3e 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -76,7 +76,6 @@ TEST(scan_test, separator) { EXPECT_EQ(n2, 20); } -#ifdef FMT_HAVE_STRPTIME struct num { int value; }; @@ -93,10 +92,9 @@ template <> struct scanner { } template - auto scan(num&, ScanContext& ctx) const -> typename ScanContext::iterator { - // TODO - // return fmt::scan({ctx.begin(), ctx.end()}, "{}", n.value); - return ctx.begin(); + auto scan(num& n, ScanContext& ctx) const -> typename ScanContext::iterator { + // TODO: handle specifier + return fmt::scan(ctx, "{}", n.value); } }; } // namespace fmt @@ -105,9 +103,8 @@ TEST(scan_test, read_custom) { auto input = "42"; auto n = num(); fmt::scan(input, "{:}", n); - // EXPECT_EQ(n, 42); + EXPECT_EQ(n.value, 42); } -#endif TEST(scan_test, invalid_format) { EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error, diff --git a/test/scan.h b/test/scan.h index c03356a9..495ec628 100644 --- a/test/scan.h +++ b/test/scan.h @@ -94,6 +94,10 @@ class scan_buffer { value_ = *buf->ptr_; } + friend scan_buffer& get_buffer(iterator it) { + return *it.buf_; + } + public: iterator() : ptr_(sentinel()), buf_(nullptr) {} @@ -154,7 +158,7 @@ class string_scan_buffer : public scan_buffer { #ifdef _WIN32 void flockfile(FILE* f) { _lock_file(f); } void funlockfile(FILE* f) { _unlock_file(f); } -int getc_unlocked(FILE *f) { return _fgetc_nolock(f); } +int getc_unlocked(FILE* f) { return _fgetc_nolock(f); } #endif // A FILE wrapper. F is FILE defined as a template parameter to make @@ -534,6 +538,15 @@ auto scan(string_view input, string_view fmt, T&... args) return input.begin() + (buf.begin().base() - input.data()); } +template ::value)> +auto scan(InputRange&& input, string_view fmt, T&... args) + -> decltype(std::begin(input)) { + auto it = std::begin(input); + vscan(get_buffer(it), fmt, make_scan_args(args...)); + return it; +} + template bool scan(std::FILE* f, string_view fmt, T&... args) { auto&& buf = detail::file_scan_buffer(f); vscan(buf, fmt, make_scan_args(args...)); -- cgit v1.2.3 From bfba2f9e923d67af34be88e207fb8878350734c6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 06:34:49 -0800 Subject: Improve iterator handling in scan --- test/scan.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/test/scan.h b/test/scan.h index 495ec628..14971d3c 100644 --- a/test/scan.h +++ b/test/scan.h @@ -395,8 +395,10 @@ struct scan_handler : error_handler { int next_arg_id_; scan_arg arg_; - template auto read_uint() -> optional { - auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + using iterator = scan_buffer::iterator; + + template auto read_uint(iterator& it) -> optional { + auto end = scan_ctx_.end(); if (it == end) return {}; char c = *it; if (c < '0' || c > '9') on_error("invalid input"); @@ -412,7 +414,6 @@ struct scan_handler : error_handler { ++num_digits; if (c < '0' || c > '9') break; } while (it != end); - scan_ctx_.advance_to(it); // Check overflow. if (num_digits <= std::numeric_limits::digits10) return value; @@ -424,14 +425,11 @@ struct scan_handler : error_handler { throw format_error("number is too big"); } - template auto read_int() -> optional { - auto it = scan_ctx_.begin(), end = scan_ctx_.end(); + template auto read_int(iterator& it) -> optional { + auto end = scan_ctx_.end(); bool negative = it != end && *it == '-'; - if (negative) { - ++it; - scan_ctx_.advance_to(it); - } - if (auto abs_value = read_uint::type>()) { + if (negative) ++it; + if (auto abs_value = read_uint::type>(it)) { auto value = static_cast(*abs_value); return negative ? -value : value; } @@ -469,24 +467,22 @@ struct scan_handler : error_handler { void on_replacement_field(int, const char*) { auto it = scan_ctx_.begin(), end = scan_ctx_.end(); while (it != end && is_whitespace(*it)) ++it; - scan_ctx_.advance_to(it); switch (arg_.type) { case scan_type::int_type: - if (auto value = read_int()) *arg_.int_value = *value; + if (auto value = read_int(it)) *arg_.int_value = *value; break; case scan_type::uint_type: - if (auto value = read_uint()) *arg_.uint_value = *value; + if (auto value = read_uint(it)) *arg_.uint_value = *value; break; case scan_type::long_long_type: - if (auto value = read_int()) *arg_.long_long_value = *value; + if (auto value = read_int(it)) *arg_.long_long_value = *value; break; case scan_type::ulong_long_type: - if (auto value = read_uint()) + if (auto value = read_uint(it)) *arg_.ulong_long_value = *value; break; case scan_type::string_type: while (it != end && *it != ' ') arg_.string->push_back(*it++); - scan_ctx_.advance_to(it); break; case scan_type::string_view_type: { auto range = to_contiguous(it); @@ -497,13 +493,13 @@ struct scan_handler : error_handler { size_t size = to_unsigned(p - range.begin); *arg_.string_view = {range.begin, size}; advance(it, size); - scan_ctx_.advance_to(it); break; } case scan_type::none_type: case scan_type::custom_type: assert(false); } + scan_ctx_.advance_to(it); } auto on_format_specs(int, const char* begin, const char*) -> const char* { -- cgit v1.2.3 From d83c1b8d4a519d8140b3ce13ac948f0fb835867f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 07:15:02 -0800 Subject: Add initial specifier support to scan --- test/scan.h | 48 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/test/scan.h b/test/scan.h index 14971d3c..e368f37b 100644 --- a/test/scan.h +++ b/test/scan.h @@ -387,6 +387,34 @@ struct scan_args { namespace detail { +const char* parse_scan_specs( + const char* begin, const char* end, format_specs<>& specs, scan_type) { + while (begin != end) { + switch (to_ascii(*begin)) { + // TODO: parse scan format specifiers + case 'x': + specs.type = presentation_type::hex_lower; + break; + case '}': + return begin; + } + } + return begin; +} + +struct arg_scanner { + using iterator = scan_buffer::iterator; + + iterator begin; + iterator end; + const format_specs<>& specs; + + template + auto operator()(T) -> iterator { + return begin; + } +}; + struct scan_handler : error_handler { private: scan_parse_context parse_ctx_; @@ -502,11 +530,21 @@ struct scan_handler : error_handler { scan_ctx_.advance_to(it); } - auto on_format_specs(int, const char* begin, const char*) -> const char* { - if (arg_.type != scan_type::custom_type) return begin; - parse_ctx_.advance_to(begin); - arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); - return parse_ctx_.begin(); + auto on_format_specs(int, const char* begin, const char* end) -> const char* { + if (arg_.type == scan_type::custom_type) { + parse_ctx_.advance_to(begin); + arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_); + return parse_ctx_.begin(); + } + auto specs = format_specs<>(); + begin = parse_scan_specs(begin, end, specs, arg_.type); + if (begin == end || *begin != '}') + on_error("missing '}' in format string"); + auto s = arg_scanner{scan_ctx_.begin(), scan_ctx_.end(), specs}; + // TODO: scan argument according to specs + (void)s; + //context.advance_to(visit_format_arg(s, arg)); + return begin; } void on_error(const char* message) { -- cgit v1.2.3 From d5823aae36fcabc683d34b8b9cfd51e9653e6569 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 08:11:29 -0800 Subject: Ceci n'est pas une pipe --- include/fmt/os.h | 1 + test/scan-test.cc | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/fmt/os.h b/include/fmt/os.h index c81955d7..3c7b3ccb 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -315,6 +315,7 @@ class FMT_API file { // Creates a pipe setting up read_end and write_end file objects for reading // and writing respectively. + // DEPRECATED! Taking files as out parameters is deprecated. static void pipe(file& read_end, file& write_end); // Creates a buffered_file object associated with this file and detaches diff --git a/test/scan-test.cc b/test/scan-test.cc index e2bfea3e..8c84ab06 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -143,6 +143,7 @@ TEST(scan_test, file) { } TEST(scan_test, lock) { + fmt::file read_end, write_end; fmt::file::pipe(read_end, write_end); @@ -152,6 +153,7 @@ TEST(scan_test, lock) { write_end.close(); }); + std::atomic count = 0; fmt::buffered_file f = read_end.fdopen("r"); auto fun = [&]() { int value = 0; @@ -161,6 +163,7 @@ TEST(scan_test, lock) { EXPECT_EQ(value, 42); break; } + ++count; } }; std::thread consumer1(fun); @@ -168,5 +171,7 @@ TEST(scan_test, lock) { producer.join(); consumer1.join(); consumer2.join(); + EXPECT_EQ(count, 1000); + } #endif // FMT_USE_FCNTL -- cgit v1.2.3 From 662d784157cf0e96da1c93c6624c33661273641b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 08:21:10 -0800 Subject: Fix scan test --- test/scan-test.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/scan-test.cc b/test/scan-test.cc index 8c84ab06..e2bfea3e 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -143,7 +143,6 @@ TEST(scan_test, file) { } TEST(scan_test, lock) { - fmt::file read_end, write_end; fmt::file::pipe(read_end, write_end); @@ -153,7 +152,6 @@ TEST(scan_test, lock) { write_end.close(); }); - std::atomic count = 0; fmt::buffered_file f = read_end.fdopen("r"); auto fun = [&]() { int value = 0; @@ -163,7 +161,6 @@ TEST(scan_test, lock) { EXPECT_EQ(value, 42); break; } - ++count; } }; std::thread consumer1(fun); @@ -171,7 +168,5 @@ TEST(scan_test, lock) { producer.join(); consumer1.join(); consumer2.join(); - EXPECT_EQ(count, 1000); - } #endif // FMT_USE_FCNTL -- cgit v1.2.3 From 4a6f0be5b63fcdbc981a91d14768c1eff2c9cd32 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 08:22:55 -0800 Subject: Improve scan test --- test/scan-test.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/scan-test.cc b/test/scan-test.cc index e2bfea3e..b190fd57 100644 --- a/test/scan-test.cc +++ b/test/scan-test.cc @@ -152,6 +152,7 @@ TEST(scan_test, lock) { write_end.close(); }); + std::atomic count(0); fmt::buffered_file f = read_end.fdopen("r"); auto fun = [&]() { int value = 0; @@ -161,12 +162,16 @@ TEST(scan_test, lock) { EXPECT_EQ(value, 42); break; } + ++count; } }; std::thread consumer1(fun); std::thread consumer2(fun); + producer.join(); consumer1.join(); consumer2.join(); + EXPECT_EQ(count, 1000); + } #endif // FMT_USE_FCNTL -- cgit v1.2.3 From e206043d2bf7c19ed3dcf6eb6af35818093e4f55 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 09:00:05 -0800 Subject: Update changelog --- ChangeLog.md | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 2a0fad32..10d9fed1 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -24,8 +24,8 @@ https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib. - Added a formatter for `std::source_location` - (https://github.com/fmtlib/fmt/pull/3730). For example - ([godbolt](https://godbolt.org/z/YajfKjhhr)): + (https://github.com/fmtlib/fmt/pull/3730). + For example ([godbolt](https://godbolt.org/z/YajfKjhhr)): ```c++ #include @@ -75,6 +75,14 @@ Thanks @js324. +- Made `format_as` work with references + (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov. + +- Disallowed the `c` specifier for `bool`. + +- Fixed localized formatting in bases other than decimal + (https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. + - Added synchronization with the underlying output stream when writing to the Windows console (https://github.com/fmtlib/fmt/pull/3668, @@ -89,12 +97,16 @@ - Made `fmt::streamed` `constexpr`. (https://github.com/fmtlib/fmt/pull/3650). Thanks @muggenhor. +- Enabled `consteval` on older versions of MSVC + (https://github.com/fmtlib/fmt/pull/3757). Thanks @phprus. + - Added an option to build without `wchar_t` support on Windows (https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. -- Improved build configuration +- Improved build and CI configuration (https://github.com/fmtlib/fmt/issues/3701, - https://github.com/fmtlib/fmt/pull/3702). Thanks @pklima. + https://github.com/fmtlib/fmt/pull/3702, + https://github.com/fmtlib/fmt/pull/3749). Thanks @pklima and @tchaikov. - Fixed various warnings and compilation issues (https://github.com/fmtlib/fmt/pull/3610, @@ -113,10 +125,24 @@ https://github.com/fmtlib/fmt/issues/3712, https://github.com/fmtlib/fmt/pull/3713, https://github.com/fmtlib/fmt/pull/3716, - https://github.com/fmtlib/fmt/pull/3723). + https://github.com/fmtlib/fmt/pull/3723, + https://github.com/fmtlib/fmt/issues/3738, + https://github.com/fmtlib/fmt/issues/3740, + https://github.com/fmtlib/fmt/pull/3741, + https://github.com/fmtlib/fmt/pull/3743, + https://github.com/fmtlib/fmt/issues/3745, + https://github.com/fmtlib/fmt/pull/3747, + https://github.com/fmtlib/fmt/pull/3748, + https://github.com/fmtlib/fmt/pull/3751, + https://github.com/fmtlib/fmt/pull/3754, + https://github.com/fmtlib/fmt/pull/3755, + https://github.com/fmtlib/fmt/issues/3760, + https://github.com/fmtlib/fmt/pull/3762, + https://github.com/fmtlib/fmt/pull/3764). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, - @cptFracassa, @kuguma, @PeterJohnson . + @cptFracassa, @kuguma, @PeterJohnson, @H1X4Dev, @asantoni, @eltociear, + @msimberg, @tchaikov. - Improved documentation and README (https://github.com/fmtlib/fmt/pull/3642, @@ -124,8 +150,10 @@ https://github.com/fmtlib/fmt/pull/3655, https://github.com/fmtlib/fmt/pull/3661, https://github.com/fmtlib/fmt/issues/3673, - https://github.com/fmtlib/fmt/pull/3677). - Thanks @idzm, @perlun, @joycebrum, @fennewald. + https://github.com/fmtlib/fmt/pull/3677, + https://github.com/fmtlib/fmt/pull/3737, + https://github.com/fmtlib/fmt/pull/3744). + Thanks @idzm, @perlun, @joycebrum, @fennewald, @reinhardt1053, @GeorgeLS. - Updated CI dependencies (https://github.com/fmtlib/fmt/pull/3615, @@ -133,7 +161,8 @@ https://github.com/fmtlib/fmt/pull/3623, https://github.com/fmtlib/fmt/pull/3666, https://github.com/fmtlib/fmt/pull/3696, - https://github.com/fmtlib/fmt/pull/3697). + https://github.com/fmtlib/fmt/pull/3697, + https://github.com/fmtlib/fmt/pull/3759). # 10.1.1 - 2023-08-28 -- cgit v1.2.3 From a8bed38952c9b0400a9d53e02896a910cc90754d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 09:57:29 -0800 Subject: Update changelog --- ChangeLog.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 10d9fed1..309e4f28 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -6,7 +6,8 @@ - Added support for the chrono suffix for days and changed the suffix for minutes from "m" to the correct "min" - (https://github.com/fmtlib/fmt/pull/3664). + (https://github.com/fmtlib/fmt/issues/3662, + https://github.com/fmtlib/fmt/pull/3664). For example ([godbolt](https://godbolt.org/z/9KhMnq9ba)): ```c++ @@ -78,11 +79,25 @@ - Made `format_as` work with references (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov. -- Disallowed the `c` specifier for `bool`. +- Disallowed unsafe uses of `fmt::styled` + (https://github.com/fmtlib/fmt/issues/3625): + + ```c++ + auto s = fmt::styled(std::string("dangler"), fmt::emphasis::bold); + fmt::print("{}\n", s); // compile error + ``` + + Pass `fmt::styled(...)` as a parameter instead. + +- Disallowed the `c` specifier for `bool` + (https://github.com/fmtlib/fmt/pull/3734). Thanks @js324. - Fixed localized formatting in bases other than decimal (https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. +- Fixed a performance regression in experimental `fmt::ostream::print` + (https://github.com/fmtlib/fmt/issues/3674). + - Added synchronization with the underlying output stream when writing to the Windows console (https://github.com/fmtlib/fmt/pull/3668, @@ -101,7 +116,8 @@ (https://github.com/fmtlib/fmt/pull/3757). Thanks @phprus. - Added an option to build without `wchar_t` support on Windows - (https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. + (https://github.com/fmtlib/fmt/issues/3631, + https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. - Improved build and CI configuration (https://github.com/fmtlib/fmt/issues/3701, @@ -109,12 +125,14 @@ https://github.com/fmtlib/fmt/pull/3749). Thanks @pklima and @tchaikov. - Fixed various warnings and compilation issues - (https://github.com/fmtlib/fmt/pull/3610, + (https://github.com/fmtlib/fmt/issues/3607, + https://github.com/fmtlib/fmt/pull/3610, https://github.com/fmtlib/fmt/pull/3624, https://github.com/fmtlib/fmt/pull/3630, https://github.com/fmtlib/fmt/pull/3634, https://github.com/fmtlib/fmt/pull/3638, https://github.com/fmtlib/fmt/issues/3645, + https://github.com/fmtlib/fmt/issues/3646, https://github.com/fmtlib/fmt/pull/3647, https://github.com/fmtlib/fmt/pull/3652, https://github.com/fmtlib/fmt/issues/3654, @@ -145,7 +163,9 @@ @msimberg, @tchaikov. - Improved documentation and README - (https://github.com/fmtlib/fmt/pull/3642, + (https://github.com/fmtlib/fmt/issues/2086, + https://github.com/fmtlib/fmt/issues/3637, + https://github.com/fmtlib/fmt/pull/3642, https://github.com/fmtlib/fmt/pull/3653, https://github.com/fmtlib/fmt/pull/3655, https://github.com/fmtlib/fmt/pull/3661, @@ -553,7 +573,9 @@ Thanks @ShawnZhong. -- Added a formatter for `std::optional` to `fmt/std.h`. +- Added a formatter for `std::optional` to `fmt/std.h` + (https://github.com/fmtlib/fmt/issues/1367, + https://github.com/fmtlib/fmt/pull/3303). Thanks @tom-huntington. - Fixed formatting of valueless by exception variants -- cgit v1.2.3 From 47a0eec2e8a80a004e66aa571506d5ae364304b7 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 09:59:55 -0800 Subject: Remove unnecessary alias --- include/fmt/chrono.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 6a3d38ff..5a31930e 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -409,8 +409,7 @@ inline void do_write(buffer& buf, const std::tm& time, auto&& format_buf = formatbuf>(buf); auto&& os = std::basic_ostream(&format_buf); os.imbue(loc); - using iterator = std::ostreambuf_iterator; - const auto& facet = std::use_facet>(loc); + const auto& facet = std::use_facet>(loc); auto end = facet.put(os, os, Char(' '), &time, format, modifier); if (end.failed()) FMT_THROW(format_error("failed to format time")); } -- cgit v1.2.3 From a13d1b12e5af8d8dfe276c21b67135cc86cd969d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 13:00:12 -0800 Subject: Update changelog and docs --- ChangeLog.md | 36 ++++++++++++++++++++++++++++++------ doc/syntax.rst | 7 ++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 309e4f28..04a01a50 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -29,7 +29,8 @@ For example ([godbolt](https://godbolt.org/z/YajfKjhhr)): ```c++ - #include + #include + #include int main() { fmt::print("{}\n", std::source_location::current()); @@ -79,6 +80,12 @@ - Made `format_as` work with references (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov. +- Fixed formatting of invalid UTF-8 with precision + (https://github.com/fmtlib/fmt/issues/3284). + +- Fixed an inconsistency between `fmt::to_string` and `fmt::format` + (https://github.com/fmtlib/fmt/issues/3684). + - Disallowed unsafe uses of `fmt::styled` (https://github.com/fmtlib/fmt/issues/3625): @@ -89,11 +96,20 @@ Pass `fmt::styled(...)` as a parameter instead. +- Added a null check when formatting C strings with the `s` specifier + (https://github.com/fmtlib/fmt/issues/3706). + - Disallowed the `c` specifier for `bool` - (https://github.com/fmtlib/fmt/pull/3734). Thanks @js324. + (https://github.com/fmtlib/fmt/issues/3726, + https://github.com/fmtlib/fmt/pull/3734). Thanks @js324. + +- Made the default formatting unlocalized in `fmt::ostream_formatter` for + consistency with the rest of the library + (https://github.com/fmtlib/fmt/issues/3460). - Fixed localized formatting in bases other than decimal - (https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. + (https://github.com/fmtlib/fmt/issues/3693, + https://github.com/fmtlib/fmt/pull/3750). Thanks @js324. - Fixed a performance regression in experimental `fmt::ostream::print` (https://github.com/fmtlib/fmt/issues/3674). @@ -124,7 +140,7 @@ https://github.com/fmtlib/fmt/pull/3702, https://github.com/fmtlib/fmt/pull/3749). Thanks @pklima and @tchaikov. -- Fixed various warnings and compilation issues +- Fixed various warnings, compilation and test issues (https://github.com/fmtlib/fmt/issues/3607, https://github.com/fmtlib/fmt/pull/3610, https://github.com/fmtlib/fmt/pull/3624, @@ -137,11 +153,16 @@ https://github.com/fmtlib/fmt/pull/3652, https://github.com/fmtlib/fmt/issues/3654, https://github.com/fmtlib/fmt/pull/3663, + https://github.com/fmtlib/fmt/issues/3670, https://github.com/fmtlib/fmt/pull/3680, + https://github.com/fmtlib/fmt/issues/3694, https://github.com/fmtlib/fmt/pull/3695, - https://github.com/fmtlib/fmt/pull/369, + https://github.com/fmtlib/fmt/pull/3699, + https://github.com/fmtlib/fmt/issues/3705, + https://github.com/fmtlib/fmt/issues/3710, https://github.com/fmtlib/fmt/issues/3712, https://github.com/fmtlib/fmt/pull/3713, + https://github.com/fmtlib/fmt/issues/3714, https://github.com/fmtlib/fmt/pull/3716, https://github.com/fmtlib/fmt/pull/3723, https://github.com/fmtlib/fmt/issues/3738, @@ -156,7 +177,9 @@ https://github.com/fmtlib/fmt/pull/3755, https://github.com/fmtlib/fmt/issues/3760, https://github.com/fmtlib/fmt/pull/3762, - https://github.com/fmtlib/fmt/pull/3764). + https://github.com/fmtlib/fmt/issues/3763, + https://github.com/fmtlib/fmt/pull/3764, + https://github.com/fmtlib/fmt/issues/3774). Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc, @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning, @cptFracassa, @kuguma, @PeterJohnson, @H1X4Dev, @asantoni, @eltociear, @@ -172,6 +195,7 @@ https://github.com/fmtlib/fmt/issues/3673, https://github.com/fmtlib/fmt/pull/3677, https://github.com/fmtlib/fmt/pull/3737, + https://github.com/fmtlib/fmt/issues/3742, https://github.com/fmtlib/fmt/pull/3744). Thanks @idzm, @perlun, @joycebrum, @fennewald, @reinhardt1053, @GeorgeLS. diff --git a/doc/syntax.rst b/doc/syntax.rst index 53385d87..3c21902c 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -229,9 +229,10 @@ The available integer presentation types are: | none | The same as ``'d'``. | +---------+----------------------------------------------------------+ -Integer presentation types can also be used with character and Boolean values. -Boolean values are formatted using textual representation, either ``true`` or -``false``, if the presentation type is not specified. +Integer presentation types can also be used with character and Boolean values +with the only exception that ``'c'`` cannot be used with `bool`. Boolean values +are formatted using textual representation, either ``true`` or ``false``, if the +presentation type is not specified. The available presentation types for floating-point values are: -- cgit v1.2.3 From 2e6bb706bf50bdc9af3cfed3c05f1efa32e3af5b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 15:48:09 -0800 Subject: Update changelog --- ChangeLog.md | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 04a01a50..913a2fd6 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -60,6 +60,37 @@ Thanks @muggenhor. +- Added an experimental `nested_formatter` that provides an easy way applying + a formatter to one or more subobjects while automatically handling width, + fill and alignment. For example: + + ```c++ + #include + + struct point { + double x, y; + }; + + template <> + struct fmt::formatter : nested_formatter { + auto format(point p, format_context& ctx) const { + return write_padded(ctx, [=](auto out) { + return format_to(out, "({}, {})", nested(p.x), nested(p.y)); + }); + } + }; + + int main() { + fmt::print("[{:>20.2f}]", point{1, 2}); + } + ``` + + prints + + ``` + [ (1.00, 2.00)] + ``` + - Added the generic representation (`g`) to `std::filesystem::path` (https://github.com/fmtlib/fmt/issues/3715, https://github.com/fmtlib/fmt/pull/3729). For example: @@ -136,9 +167,11 @@ https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm. - Improved build and CI configuration - (https://github.com/fmtlib/fmt/issues/3701, + (https://github.com/fmtlib/fmt/pull/3679, + https://github.com/fmtlib/fmt/issues/3701, https://github.com/fmtlib/fmt/pull/3702, - https://github.com/fmtlib/fmt/pull/3749). Thanks @pklima and @tchaikov. + https://github.com/fmtlib/fmt/pull/3749). + Thanks @jcar87, @pklima and @tchaikov. - Fixed various warnings, compilation and test issues (https://github.com/fmtlib/fmt/issues/3607, -- cgit v1.2.3 From 8c520b4fdcf556a3167bcb91ab51aed17f039853 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 15:48:25 -0800 Subject: Fix comment --- include/fmt/chrono.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 5a31930e..30d68dc3 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1221,8 +1221,7 @@ class tm_writer { return static_cast(l); } - // Algorithm: - // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { const auto prev_year = curr_year - 1; const auto curr_p = -- cgit v1.2.3 From c64edcd325aefa5c66608884e25951c64caa261d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 15:51:16 -0800 Subject: Fix grammar --- ChangeLog.md | 6 +++--- doc/api.rst | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 913a2fd6..df0f7ddc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -60,9 +60,9 @@ Thanks @muggenhor. -- Added an experimental `nested_formatter` that provides an easy way applying - a formatter to one or more subobjects while automatically handling width, - fill and alignment. For example: +- Added an experimental `nested_formatter` that provides an easy way of + applying a formatter to one or more subobjects while automatically handling + width, fill and alignment. For example: ```c++ #include diff --git a/doc/api.rst b/doc/api.rst index 8b8b19c8..aeb13b96 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -167,8 +167,8 @@ for example will return ``" blue"``. -The experimental ``nested_formatter`` provides an easy way applying a formatter -to one or more subobjects. +The experimental ``nested_formatter`` provides an easy way of applying a +formatter to one or more subobjects. For example:: -- cgit v1.2.3 From 63e4b93cfc4ea38221065ceadc18b5c434dd298c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 15:54:50 -0800 Subject: Update changelog --- ChangeLog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index df0f7ddc..49b28cb7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -121,13 +121,13 @@ (https://github.com/fmtlib/fmt/issues/3625): ```c++ - auto s = fmt::styled(std::string("dangler"), fmt::emphasis::bold); + auto s = fmt::styled(std::string("dangle"), fmt::emphasis::bold); fmt::print("{}\n", s); // compile error ``` Pass `fmt::styled(...)` as a parameter instead. -- Added a null check when formatting C strings with the `s` specifier +- Added a null check when formatting a C string with the `s` specifier (https://github.com/fmtlib/fmt/issues/3706). - Disallowed the `c` specifier for `bool` -- cgit v1.2.3 From 55190dadb5f844f6ef91f5dd45160470f95d5d9e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 29 Dec 2023 16:34:42 -0800 Subject: Cleanup chrono test --- test/chrono-test.cc | 298 ++++++++++++++++++++++++++-------------------------- 1 file changed, 149 insertions(+), 149 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 3ff1c5b5..d0c87dbb 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -339,14 +339,14 @@ TEST(chrono_test, system_clock_time_point) { auto t = std::chrono::system_clock::to_time_t(t1); auto tm = *std::gmtime(&t); - EXPECT_EQ("+0000", fmt::format("{:%z}", t1)); - EXPECT_EQ("+0000", fmt::format("{:%z}", tm)); + EXPECT_EQ(fmt::format("{:%z}", t1), "+0000"); + EXPECT_EQ(fmt::format("{:%z}", tm), "+0000"); - EXPECT_EQ("+00:00", fmt::format("{:%Ez}", t1)); - EXPECT_EQ("+00:00", fmt::format("{:%Ez}", tm)); + EXPECT_EQ(fmt::format("{:%Ez}", t1), "+00:00"); + EXPECT_EQ(fmt::format("{:%Ez}", tm), "+00:00"); - EXPECT_EQ("+00:00", fmt::format("{:%Oz}", t1)); - EXPECT_EQ("+00:00", fmt::format("{:%Oz}", tm)); + EXPECT_EQ(fmt::format("{:%Oz}", t1), "+00:00"); + EXPECT_EQ(fmt::format("{:%Oz}", tm), "+00:00"); } } @@ -431,122 +431,122 @@ TEST(chrono_test, local_system_clock_time_point) { #ifndef FMT_STATIC_THOUSANDS_SEPARATOR TEST(chrono_test, format_default) { - EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); - EXPECT_EQ("42as", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42fs", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42ps", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42))); - EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42))); - EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42))); - EXPECT_EQ("42cs", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42ds", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42))); - EXPECT_EQ("42das", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42hs", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42ks", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42Ms", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42Gs", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42Ts", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42Ps", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42Es", - fmt::format("{}", std::chrono::duration(42))); - EXPECT_EQ("42min", fmt::format("{}", std::chrono::minutes(42))); - EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42))); - EXPECT_EQ("42d", fmt::format("{}", days(42))); + EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42as"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42fs"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42ps"); + EXPECT_EQ(fmt::format("{}", std::chrono::nanoseconds(42)), "42ns"); + EXPECT_EQ(fmt::format("{}", std::chrono::microseconds(42)), "42µs"); + EXPECT_EQ(fmt::format("{}", std::chrono::milliseconds(42)), "42ms"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42cs"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42ds"); + EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42das"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42hs"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42ks"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42Ms"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42Gs"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42Ts"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42Ps"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(42)), + "42Es"); + EXPECT_EQ(fmt::format("{}", std::chrono::minutes(42)), "42min"); + EXPECT_EQ(fmt::format("{}", std::chrono::hours(42)), "42h"); + EXPECT_EQ(fmt::format("{}", days(42)), "42d"); EXPECT_EQ( - "42[15]s", - fmt::format("{}", std::chrono::duration>(42))); + fmt::format("{}", std::chrono::duration>(42)), + "42[15]s"); EXPECT_EQ( - "42[15/4]s", - fmt::format("{}", std::chrono::duration>(42))); + fmt::format("{}", std::chrono::duration>(42)), + "42[15/4]s"); } TEST(chrono_test, duration_align) { auto s = std::chrono::seconds(42); - EXPECT_EQ("42s ", fmt::format("{:5}", s)); - EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5)); - EXPECT_EQ(" 42s", fmt::format("{:>5}", s)); - EXPECT_EQ("**42s**", fmt::format("{:*^7}", s)); - EXPECT_EQ("03:25:45 ", - fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345))); - EXPECT_EQ(" 03:25:45", - fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345))); - EXPECT_EQ("~~03:25:45~~", - fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345))); - EXPECT_EQ("03:25:45 ", - fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12)); + EXPECT_EQ(fmt::format("{:5}", s), "42s "); + EXPECT_EQ(fmt::format("{:{}}", s, 5), "42s "); + EXPECT_EQ(fmt::format("{:>5}", s), " 42s"); + EXPECT_EQ(fmt::format("{:*^7}", s), "**42s**"); + EXPECT_EQ(fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)), + "03:25:45 "); + EXPECT_EQ(fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)), + " 03:25:45"); + EXPECT_EQ(fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)), + "~~03:25:45~~"); + EXPECT_EQ(fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12), + "03:25:45 "); } TEST(chrono_test, tm_align) { auto t = make_tm(1975, 12, 29, 12, 14, 16); - EXPECT_EQ("1975-12-29 12:14:16", fmt::format("{:%F %T}", t)); - EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:30%F %T}", t)); - EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:{}%F %T}", t, 30)); - EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:<30%F %T}", t)); - EXPECT_EQ(" 1975-12-29 12:14:16 ", fmt::format("{:^30%F %T}", t)); - EXPECT_EQ(" 1975-12-29 12:14:16", fmt::format("{:>30%F %T}", t)); + EXPECT_EQ(fmt::format("{:%F %T}", t), "1975-12-29 12:14:16"); + EXPECT_EQ(fmt::format("{:30%F %T}", t), "1975-12-29 12:14:16 "); + EXPECT_EQ(fmt::format("{:{}%F %T}", t, 30), "1975-12-29 12:14:16 "); + EXPECT_EQ(fmt::format("{:<30%F %T}", t), "1975-12-29 12:14:16 "); + EXPECT_EQ(fmt::format("{:^30%F %T}", t), " 1975-12-29 12:14:16 "); + EXPECT_EQ(fmt::format("{:>30%F %T}", t), " 1975-12-29 12:14:16"); - EXPECT_EQ("1975-12-29 12:14:16***********", fmt::format("{:*<30%F %T}", t)); - EXPECT_EQ("*****1975-12-29 12:14:16******", fmt::format("{:*^30%F %T}", t)); - EXPECT_EQ("***********1975-12-29 12:14:16", fmt::format("{:*>30%F %T}", t)); + EXPECT_EQ(fmt::format("{:*<30%F %T}", t), "1975-12-29 12:14:16***********"); + EXPECT_EQ(fmt::format("{:*^30%F %T}", t), "*****1975-12-29 12:14:16******"); + EXPECT_EQ(fmt::format("{:*>30%F %T}", t), "***********1975-12-29 12:14:16"); } TEST(chrono_test, tp_align) { auto tp = std::chrono::time_point_cast( std::chrono::system_clock::from_time_t(0)); - EXPECT_EQ("00:00.000000", fmt::format("{:%M:%S}", tp)); - EXPECT_EQ("00:00.000000 ", fmt::format("{:15%M:%S}", tp)); - EXPECT_EQ("00:00.000000 ", fmt::format("{:{}%M:%S}", tp, 15)); - EXPECT_EQ("00:00.000000 ", fmt::format("{:<15%M:%S}", tp)); - EXPECT_EQ(" 00:00.000000 ", fmt::format("{:^15%M:%S}", tp)); - EXPECT_EQ(" 00:00.000000", fmt::format("{:>15%M:%S}", tp)); + EXPECT_EQ(fmt::format("{:%M:%S}", tp), "00:00.000000"); + EXPECT_EQ(fmt::format("{:15%M:%S}", tp), "00:00.000000 "); + EXPECT_EQ(fmt::format("{:{}%M:%S}", tp, 15), "00:00.000000 "); + EXPECT_EQ(fmt::format("{:<15%M:%S}", tp), "00:00.000000 "); + EXPECT_EQ(fmt::format("{:^15%M:%S}", tp), " 00:00.000000 "); + EXPECT_EQ(fmt::format("{:>15%M:%S}", tp), " 00:00.000000"); - EXPECT_EQ("00:00.000000***", fmt::format("{:*<15%M:%S}", tp)); - EXPECT_EQ("*00:00.000000**", fmt::format("{:*^15%M:%S}", tp)); - EXPECT_EQ("***00:00.000000", fmt::format("{:*>15%M:%S}", tp)); + EXPECT_EQ(fmt::format("{:*<15%M:%S}", tp), "00:00.000000***"); + EXPECT_EQ(fmt::format("{:*^15%M:%S}", tp), "*00:00.000000**"); + EXPECT_EQ(fmt::format("{:*>15%M:%S}", tp), "***00:00.000000"); } TEST(chrono_test, format_specs) { - EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0))); - EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0))); - EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0))); - EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0))); - EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60))); - EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42))); - EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234))); - EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0))); - EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60))); - EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42))); - EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61))); - EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0))); - EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24))); - EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14))); - EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61))); - EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0))); - EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12))); - EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24))); - EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4))); - EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14))); - EXPECT_EQ("12345", fmt::format("{:%j}", days(12345))); - EXPECT_EQ("12345", fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12))); - EXPECT_EQ("03:25:45", - fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); - EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345))); - EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345))); - EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345))); - EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345))); + EXPECT_EQ(fmt::format("{:%%}", std::chrono::seconds(0)), "%"); + EXPECT_EQ(fmt::format("{:%n}", std::chrono::seconds(0)), "\n"); + EXPECT_EQ(fmt::format("{:%t}", std::chrono::seconds(0)), "\t"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(0)), "00"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(60)), "00"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(42)), "42"); + EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds(1234)), "01.234"); + EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(0)), "00"); + EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(60)), "00"); + EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(42)), "42"); + EXPECT_EQ(fmt::format("{:%M}", std::chrono::seconds(61)), "01"); + EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(0)), "00"); + EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(24)), "00"); + EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(14)), "14"); + EXPECT_EQ(fmt::format("{:%H}", std::chrono::minutes(61)), "01"); + EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(0)), "12"); + EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(12)), "12"); + EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(24)), "12"); + EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(4)), "04"); + EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(14)), "02"); + EXPECT_EQ(fmt::format("{:%j}", days(12345)), "12345"); + EXPECT_EQ(fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12)), "12345"); + EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)), + "03:25:45"); + EXPECT_EQ(fmt::format("{:%R}", std::chrono::seconds(12345)), "03:25"); + EXPECT_EQ(fmt::format("{:%T}", std::chrono::seconds(12345)), "03:25:45"); + EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(12345)), "12345"); + EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(12345)), "s"); } TEST(chrono_test, invalid_specs) { @@ -628,77 +628,77 @@ using dms = std::chrono::duration; TEST(chrono_test, format_default_fp) { typedef std::chrono::duration fs; - EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234))); + EXPECT_EQ(fmt::format("{}", fs(1.234)), "1.234s"); typedef std::chrono::duration fms; - EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234))); + EXPECT_EQ(fmt::format("{}", fms(1.234)), "1.234ms"); typedef std::chrono::duration ds; - EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234))); - EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234))); + EXPECT_EQ(fmt::format("{}", ds(1.234)), "1.234s"); + EXPECT_EQ(fmt::format("{}", dms(1.234)), "1.234ms"); } TEST(chrono_test, format_precision) { EXPECT_THROW_MSG( (void)fmt::format(runtime("{:.2%Q}"), std::chrono::seconds(42)), fmt::format_error, "precision not allowed for this argument type"); - EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234))); - EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); - EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2)); + EXPECT_EQ(fmt::format("{:.0}", dms(1.234)), "1ms"); + EXPECT_EQ(fmt::format("{:.1}", dms(1.234)), "1.2ms"); + EXPECT_EQ(fmt::format("{:.{}}", dms(1.234), 2), "1.23ms"); - EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56))); - EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56))); - EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56))); + EXPECT_EQ(fmt::format("{:.0}", dms(12.56)), "13ms"); + EXPECT_EQ(fmt::format("{:.1}", dms(12.56)), "12.6ms"); + EXPECT_EQ(fmt::format("{:.2}", dms(12.56)), "12.56ms"); } TEST(chrono_test, format_full_specs) { - EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234))); - EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234))); - EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2)); - EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1)); - EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8)); - EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3)); - EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234))); - - EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56))); - EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0)); - EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0)); - EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8)); - EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0)); - EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56))); + EXPECT_EQ(fmt::format("{:6.0}", dms(1.234)), "1ms "); + EXPECT_EQ(fmt::format("{:6.1}", dms(1.234)), "1.2ms "); + EXPECT_EQ(fmt::format("{:>8.{}}", dms(1.234), 2), " 1.23ms"); + EXPECT_EQ(fmt::format("{:^{}.{}}", dms(1.234), 7, 1), " 1.2ms "); + EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8), " 1.23ms "); + EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(1.234), 9, 3), "=1.234ms="); + EXPECT_EQ(fmt::format("{:*^10.4}", dms(1.234)), "*1.2340ms*"); + + EXPECT_EQ(fmt::format("{:6.0}", dms(12.56)), "13ms "); + EXPECT_EQ(fmt::format("{:>8.{}}", dms(12.56), 0), " 13ms"); + EXPECT_EQ(fmt::format("{:^{}.{}}", dms(12.56), 6, 0), " 13ms "); + EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8), " 13ms "); + EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(12.56), 9, 0), "==13ms==="); + EXPECT_EQ(fmt::format("{:*^10.0}", dms(12.56)), "***13ms***"); } TEST(chrono_test, format_simple_q) { typedef std::chrono::duration fs; - EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234))); + EXPECT_EQ(fmt::format("{:%Q %q}", fs(1.234)), "1.234 s"); typedef std::chrono::duration fms; - EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234))); + EXPECT_EQ(fmt::format("{:%Q %q}", fms(1.234)), "1.234 ms"); typedef std::chrono::duration ds; - EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234))); - EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234))); + EXPECT_EQ(fmt::format("{:%Q %q}", ds(1.234)), "1.234 s"); + EXPECT_EQ(fmt::format("{:%Q %q}", dms(1.234)), "1.234 ms"); } TEST(chrono_test, format_precision_q) { EXPECT_THROW_MSG( (void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)), fmt::format_error, "precision not allowed for this argument type"); - EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); - EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2)); + EXPECT_EQ(fmt::format("{:.1%Q %q}", dms(1.234)), "1.2 ms"); + EXPECT_EQ(fmt::format("{:.{}%Q %q}", dms(1.234), 2), "1.23 ms"); } TEST(chrono_test, format_full_specs_q) { - EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234))); - EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234))); - EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2)); - EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1)); - EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9)); - EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3)); - EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234))); - - EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56))); - EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0)); - EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0)); - EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9)); - EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0)); - EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56))); + EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(1.234)), "1 ms "); + EXPECT_EQ(fmt::format("{:7.1%Q %q}", dms(1.234)), "1.2 ms "); + EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(1.234), 2), " 1.23 ms"); + EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1), " 1.2 ms "); + EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9), " 1.23 ms "); + EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3), "=1.234 ms="); + EXPECT_EQ(fmt::format("{:*^11.4%Q %q}", dms(1.234)), "*1.2340 ms*"); + + EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(12.56)), "13 ms "); + EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(12.56), 0), " 13 ms"); + EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0), " 13 ms "); + EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9), " 13 ms "); + EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0), "==13 ms=="); + EXPECT_EQ(fmt::format("{:*^11.0%Q %q}", dms(12.56)), "***13 ms***"); } TEST(chrono_test, invalid_width_id) { @@ -936,9 +936,9 @@ TEST(chrono_test, timestamp_sub_seconds) { auto epoch = sys_time(); auto d = std::chrono::milliseconds(250); - EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - d)); - EXPECT_EQ("00.000", fmt::format("{:%S}", epoch)); - EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + d)); + EXPECT_EQ(fmt::format("{:%S}", epoch - d), "59.750"); + EXPECT_EQ(fmt::format("{:%S}", epoch), "00.000"); + EXPECT_EQ(fmt::format("{:%S}", epoch + d), "00.250"); } TEST(chrono_test, glibc_extensions) { -- cgit v1.2.3 From 3324152db41b5c422368fad701a98c09f9c49d14 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 06:53:20 -0800 Subject: Update the release script --- support/manage.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/support/manage.py b/support/manage.py index 36f61d9b..408d1062 100755 --- a/support/manage.py +++ b/support/manage.py @@ -229,12 +229,17 @@ def release(args): if not fmt_repo.update('-b', branch, fmt_repo_url): clean_checkout(fmt_repo, branch) - # Convert changelog from RST to GitHub-flavored Markdown and get the - # version. + # Update the date in the changelog. changelog = 'ChangeLog.md' changelog_path = os.path.join(fmt_repo.dir, changelog) - import rst2md - changes, version = rst2md.convert(changelog_path) + title_len = 0 + for line in fileinput.input(changelog_path, inplace=True): + m = re.match(r'# (.*) - TBD', line) + if m: + version = m.group(1) + line = version + ' - ' + datetime.date.today().isoformat() + '\n' + sys.stdout.write(line) + cmakelists = 'CMakeLists.txt' for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), inplace=True): @@ -243,18 +248,6 @@ def release(args): line = prefix + version + ')\n' sys.stdout.write(line) - # Update the version in the changelog. - title_len = 0 - for line in fileinput.input(changelog_path, inplace=True): - if line.startswith(version + ' - TBD'): - line = version + ' - ' + datetime.date.today().isoformat() - title_len = len(line) - line += '\n' - elif title_len: - line = '-' * title_len + '\n' - title_len = 0 - sys.stdout.write(line) - # Add the version to the build script. script = os.path.join('doc', 'build.py') script_path = os.path.join(fmt_repo.dir, script) -- cgit v1.2.3 From 44b76d88f48d396507b7141dbbd6c5a2d6df3415 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 07:11:13 -0800 Subject: Fix docs --- doc/api.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/api.rst b/doc/api.rst index aeb13b96..31b0328d 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -600,7 +600,6 @@ System APIs :members: .. doxygenfunction:: fmt::windows_error - :members: .. _ostream-api: -- cgit v1.2.3 From 41d31512b7c6cb849b6540b68f01746c451cbdd1 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 07:11:32 -0800 Subject: Remove unused import --- support/manage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/manage.py b/support/manage.py index 408d1062..aa1a6bc1 100755 --- a/support/manage.py +++ b/support/manage.py @@ -12,7 +12,7 @@ obtained from https://github.com/settings/tokens. from __future__ import print_function import datetime, docopt, errno, fileinput, json, os -import re, requests, shutil, sys, tempfile +import re, requests, shutil, sys from contextlib import contextmanager from distutils.version import LooseVersion from subprocess import check_call -- cgit v1.2.3 From 5ddd0cad15999439bbf96aa2e5241e9dced848ed Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 07:43:21 -0800 Subject: Add a visitor for scan arguments --- test/scan.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/scan.h b/test/scan.h index e368f37b..6217f223 100644 --- a/test/scan.h +++ b/test/scan.h @@ -363,6 +363,30 @@ class scan_arg { custom.scan = scan_custom_arg; } + template + auto visit(Visitor&& vis) -> decltype(vis(std::declval())) { + switch (type) { + case scan_type::none_type: + break; + case scan_type::int_type: + return vis(int_value); + case scan_type::uint_type: + return vis(uint_value); + case scan_type::long_long_type: + return vis(long_long_value); + case scan_type::ulong_long_type: + return vis(ulong_long_value); + case scan_type::string_type: + return vis(string); + case scan_type::string_view_type: + return vis(string_view); + case scan_type::custom_type: + // TODO: implement + break; + } + return vis(monostate()); + } + private: template static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx, -- cgit v1.2.3 From 28576b0600c0ce9183decfcd88e61c4ab6ca33db Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 09:57:31 -0800 Subject: Workaround github markdown mess --- support/manage.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/support/manage.py b/support/manage.py index aa1a6bc1..0e116010 100755 --- a/support/manage.py +++ b/support/manage.py @@ -232,12 +232,22 @@ def release(args): # Update the date in the changelog. changelog = 'ChangeLog.md' changelog_path = os.path.join(fmt_repo.dir, changelog) - title_len = 0 + first_section = True + code_block = False + changes = '' for line in fileinput.input(changelog_path, inplace=True): m = re.match(r'# (.*) - TBD', line) if m: version = m.group(1) line = version + ' - ' + datetime.date.today().isoformat() + '\n' + elif line.startswith('#'): + first_section = False + elif first_section: + changes += line + if re.match(r'^\s*```', line): + code_block = not code_block + elif not code_block and line != '\n': + changes = changes.rstrip() + ' ' sys.stdout.write(line) cmakelists = 'CMakeLists.txt' -- cgit v1.2.3 From 5f9058dbd4aa814372e54703d64ebc43fe44bba4 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 11:23:08 -0800 Subject: Improve scripts --- doc/build.py | 2 +- support/manage.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/build.py b/doc/build.py index bd10aa84..c25f588a 100755 --- a/doc/build.py +++ b/doc/build.py @@ -31,7 +31,7 @@ def create_build_env(venv_dir='virtualenv'): # Jinja2 >= 3.1 incompatible with sphinx 3.3.0 # See: https://github.com/sphinx-doc/sphinx/issues/10291 pip.install('Jinja2<3.1') - pip.install('sphinx-doc/sphinx', 'v3.3.0') + pip.install('sphinx==3.3.0') pip.install('michaeljones/breathe', 'v4.25.0') def build_docs(version='dev', **kwargs): diff --git a/support/manage.py b/support/manage.py index 0e116010..7f0c25a7 100755 --- a/support/manage.py +++ b/support/manage.py @@ -243,11 +243,12 @@ def release(args): elif line.startswith('#'): first_section = False elif first_section: - changes += line + changes_line = line if re.match(r'^\s*```', line): code_block = not code_block elif not code_block and line != '\n': - changes = changes.rstrip() + ' ' + changes_line = line.rstrip() + ' ' + changes += changes_line sys.stdout.write(line) cmakelists = 'CMakeLists.txt' -- cgit v1.2.3 From bd3273021b7992e98694cfa5b6af8da50f1e1adc Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 14:29:31 -0800 Subject: Update release script --- support/manage.py | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/support/manage.py b/support/manage.py index 7f0c25a7..b72deafd 100755 --- a/support/manage.py +++ b/support/manage.py @@ -229,27 +229,48 @@ def release(args): if not fmt_repo.update('-b', branch, fmt_repo_url): clean_checkout(fmt_repo, branch) - # Update the date in the changelog. + # Update the date in the changelog and extract the version and the first + # section content. changelog = 'ChangeLog.md' changelog_path = os.path.join(fmt_repo.dir, changelog) - first_section = True - code_block = False - changes = '' - for line in fileinput.input(changelog_path, inplace=True): - m = re.match(r'# (.*) - TBD', line) - if m: - version = m.group(1) + is_first_section = True + first_section = [] + for i, line in enumerate(fileinput.input(changelog_path, inplace=True)): + if i == 0: + version = re.match(r'# (.*) - TBD', line).group(1) line = version + ' - ' + datetime.date.today().isoformat() + '\n' + elif not is_first_section: + pass elif line.startswith('#'): - first_section = False - elif first_section: - changes_line = line - if re.match(r'^\s*```', line): - code_block = not code_block - elif not code_block and line != '\n': - changes_line = line.rstrip() + ' ' - changes += changes_line + is_first_section = False + else: + first_section.append(line) sys.stdout.write(line) + if first_section[0] == '\n': + first_section.pop(0) + + changes = '' + code_block = False + stripped = False + for line in first_section: + if re.match(r'^\s*```', line): + code_block = not code_block + changes += line + stripped = False + continue + if code_block: + changes += line + continue + if line == '\n': + changes += line + if stripped: + changes += line + stripped = False + continue + if stripped: + line = ' ' + line.lstrip() + changes += line.rstrip() + stripped = True cmakelists = 'CMakeLists.txt' for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), -- cgit v1.2.3 From 4939d67a83e494fd8edcfbaefe60053d1f4bcb4e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 15:08:27 -0800 Subject: Cleanup scripts --- doc/build.py | 9 +++++++-- support/manage.py | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/build.py b/doc/build.py index c25f588a..e60eeea0 100755 --- a/doc/build.py +++ b/doc/build.py @@ -4,7 +4,12 @@ import errno, os, re, sys from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT -versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0', '10.0.0', '10.1.0', '10.1.1', '10.1.1'] +versions = [ + '1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', + '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', + '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', + '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0'] +versions += ['10.0.0', '10.1.0', '10.1.1', '10.1.1'] class Pip: def __init__(self, venv_dir): @@ -50,7 +55,7 @@ def build_docs(version='dev', **kwargs): GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = {0}/args.h {0}/chrono.h {0}/color.h {0}/core.h \ - {0}/compile.h {0}/format.h {0}/os.h {0}/ostream.h \ + {0}/compile.h {0}/format.h {0}/os.h {0}/ostream.h \ {0}/printf.h {0}/xchar.h QUIET = YES JAVADOC_AUTOBRIEF = YES diff --git a/support/manage.py b/support/manage.py index b72deafd..55cc3bdf 100755 --- a/support/manage.py +++ b/support/manage.py @@ -284,7 +284,7 @@ def release(args): script = os.path.join('doc', 'build.py') script_path = os.path.join(fmt_repo.dir, script) for line in fileinput.input(script_path, inplace=True): - m = re.match(r'( *versions = )\[(.+)\]', line) + m = re.match(r'( *versions \+= )\[(.+)\]', line) if m: line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) sys.stdout.write(line) -- cgit v1.2.3 From 8e6b2541a6cd0b21baef8cc6daa2631326e801fe Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 30 Dec 2023 16:07:35 -0800 Subject: Apply coding conventions --- test/chrono-test.cc | 23 +++++++++++------------ test/format-test.cc | 4 ++-- test/posix-mock.h | 8 ++++---- test/printf-test.cc | 4 ++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index d0c87dbb..b2d03f97 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -627,12 +627,10 @@ TEST(chrono_test, locale) { using dms = std::chrono::duration; TEST(chrono_test, format_default_fp) { - typedef std::chrono::duration fs; - EXPECT_EQ(fmt::format("{}", fs(1.234)), "1.234s"); - typedef std::chrono::duration fms; - EXPECT_EQ(fmt::format("{}", fms(1.234)), "1.234ms"); - typedef std::chrono::duration ds; - EXPECT_EQ(fmt::format("{}", ds(1.234)), "1.234s"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(1.234)), "1.234s"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(1.234)), + "1.234ms"); + EXPECT_EQ(fmt::format("{}", std::chrono::duration(1.234)), "1.234s"); EXPECT_EQ(fmt::format("{}", dms(1.234)), "1.234ms"); } @@ -667,12 +665,13 @@ TEST(chrono_test, format_full_specs) { } TEST(chrono_test, format_simple_q) { - typedef std::chrono::duration fs; - EXPECT_EQ(fmt::format("{:%Q %q}", fs(1.234)), "1.234 s"); - typedef std::chrono::duration fms; - EXPECT_EQ(fmt::format("{:%Q %q}", fms(1.234)), "1.234 ms"); - typedef std::chrono::duration ds; - EXPECT_EQ(fmt::format("{:%Q %q}", ds(1.234)), "1.234 s"); + EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration(1.234)), + "1.234 s"); + EXPECT_EQ( + fmt::format("{:%Q %q}", std::chrono::duration(1.234)), + "1.234 ms"); + EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration(1.234)), + "1.234 s"); EXPECT_EQ(fmt::format("{:%Q %q}", dms(1.234)), "1.234 ms"); } diff --git a/test/format-test.cc b/test/format-test.cc index 0f7fb008..325bef99 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -353,9 +353,9 @@ TEST(memory_buffer_test, move_assignment) { } TEST(memory_buffer_test, grow) { - typedef allocator_ref> Allocator; + using allocator = allocator_ref>; mock_allocator alloc; - basic_memory_buffer buffer((Allocator(&alloc))); + basic_memory_buffer buffer((allocator(&alloc))); buffer.resize(7); using fmt::detail::to_unsigned; for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i; diff --git a/test/posix-mock.h b/test/posix-mock.h index 4f2a42c1..54580871 100644 --- a/test/posix-mock.h +++ b/test/posix-mock.h @@ -30,13 +30,13 @@ namespace test { #ifndef _MSC_VER // Size type for read and write. -typedef size_t size_t; -typedef ssize_t ssize_t; +using size_t = size_t; +using ssize_t = ssize_t; int open(const char* path, int oflag, int mode); int fstat(int fd, struct stat* buf); #else -typedef unsigned size_t; -typedef int ssize_t; +using size_t = unsigned; +using ssize_t = int; #endif #ifndef _WIN32 diff --git a/test/printf-test.cc b/test/printf-test.cc index 81db9b23..7e09ecca 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -310,10 +310,10 @@ TEST(printf_test, dynamic_precision) { } } -template struct make_signed { typedef T type; }; +template struct make_signed { using type = T; }; #define SPECIALIZE_MAKE_SIGNED(T, S) \ - template <> struct make_signed { typedef S type; } + template <> struct make_signed { using type = S; } SPECIALIZE_MAKE_SIGNED(char, signed char); SPECIALIZE_MAKE_SIGNED(unsigned char, signed char); -- cgit v1.2.3 From 23826669cf1fa8c6b60e4c6497aff9be36bf48ae Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 31 Dec 2023 07:37:59 -0800 Subject: Cleanup error handling --- include/fmt/core.h | 2 ++ include/fmt/format.h | 75 +++++++++++++++++++++------------------------------- test/xchar-test.cc | 2 +- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 1e0503db..6fa22d3a 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -644,6 +644,7 @@ enum { pointer_set = set(type::pointer_type) }; +// DEPRECATED! FMT_NORETURN FMT_API void throw_format_error(const char* message); struct error_handler { @@ -1762,6 +1763,7 @@ template class basic_format_context { } auto args() const -> const format_args& { return args_; } + // DEPRECATED! FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; } void on_error(const char* message) { error_handler().on_error(message); } diff --git a/include/fmt/format.h b/include/fmt/format.h index ded5d513..bc0d4762 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2430,9 +2430,8 @@ struct float_specs { bool showpoint : 1; }; -template -FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, - ErrorHandler&& eh = {}) +template +FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs) -> float_specs { auto result = float_specs(); result.showpoint = specs.alt; @@ -2468,7 +2467,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& specs, result.format = float_format::hex; break; default: - eh.on_error("invalid format specifier"); + throw_format_error("invalid format specifier"); break; } return result; @@ -3813,51 +3812,39 @@ template struct custom_formatter { template void operator()(T) const {} }; -template class width_checker { - public: - explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} - +struct width_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative width"); + if (is_negative(value)) throw_format_error("negative width"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("width is not integer"); + throw_format_error("width is not integer"); return 0; } - - private: - ErrorHandler& handler_; }; -template class precision_checker { - public: - explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {} - +struct precision_checker { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) handler_.on_error("negative precision"); + if (is_negative(value)) throw_format_error("negative precision"); return static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - handler_.on_error("precision is not integer"); + throw_format_error("precision is not integer"); return 0; } - - private: - ErrorHandler& handler_; }; -template