From fd52de0c6b6aa690ba84a30b5832e03e0a7131db Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 9 Dec 2019 07:30:34 -0800 Subject: [PATCH 1/4] Add FMT_CUDA_TEST CMake option to enable cuda-test --- CMakeLists.txt | 1 + test/CMakeLists.txt | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a17358f..3b6b79ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) option(FMT_TEST "Generate the test target." ${MASTER_PROJECT}) option(FMT_FUZZ "Generate the fuzz target." OFF) +option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) project(FMT CXX) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8aab2c91..2fee2125 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -229,20 +229,22 @@ if (FMT_PEDANTIC AND NOT WIN32) "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") endif () -# Activate optional CUDA tests if CUDA is found. For version selection, see +# Activate optional CUDA tests if CUDA is found. For version selection see # https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features -if (${CMAKE_VERSION} VERSION_LESS 3.15) - find_package(CUDA 9.0) -else () - include(CheckLanguage) - check_language(CUDA) - if (CMAKE_CUDA_COMPILER) - enable_language(CUDA OPTIONAL) - set(CUDA_FOUND TRUE) +if (FMT_CUDA_TEST) + if (${CMAKE_VERSION} VERSION_LESS 3.15) + find_package(CUDA 9.0) + else () + include(CheckLanguage) + check_language(CUDA) + if (CMAKE_CUDA_COMPILER) + enable_language(CUDA OPTIONAL) + set(CUDA_FOUND TRUE) + endif () + endif () + + if (CUDA_FOUND) + add_subdirectory(cuda-test) + add_test(NAME cuda-test COMMAND fmt-in-cuda-test) endif () endif () - -if (CUDA_FOUND) - add_subdirectory(cuda-test) - add_test(NAME cuda-test COMMAND fmt-in-cuda-test) -endif () From 9f2e7edaebccf8f271cec5e855cce1223ff3e6d6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 9 Dec 2019 13:25:08 -0800 Subject: [PATCH 2/4] Fix handling of types convertible to std::string_view --- include/fmt/core.h | 18 +++++++++--------- test/format-test.cc | 4 +--- test/ostream-test.cc | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index efe6c8e5..398253cb 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -878,9 +878,7 @@ template struct arg_mapper { FMT_ENABLE_IF( std::is_constructible, T>::value && !std::is_constructible, T>::value && - !is_string::value && - !has_formatter::value && - !has_fallback_formatter::value)> + !is_string::value && !has_formatter::value)> FMT_CONSTEXPR basic_string_view map(const T& val) { return std_string_view(val); } @@ -913,12 +911,14 @@ template struct arg_mapper { map(static_cast::type>(val))) { return map(static_cast::type>(val)); } - template ::value && !is_char::value && - !std::is_constructible, - T>::value && - (has_formatter::value || - has_fallback_formatter::value))> + template < + typename T, + FMT_ENABLE_IF( + !is_string::value && !is_char::value && + !std::is_constructible, T>::value && + (has_formatter::value || + (has_fallback_formatter::value && + !std::is_constructible, T>::value)))> FMT_CONSTEXPR const T& map(const T& val) { return val; } diff --git a/test/format-test.cc b/test/format-test.cc index fa5429f6..42ba77ba 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1691,16 +1691,14 @@ struct explicitly_convertible_to_std_string_view { }; namespace fmt { - template <> struct formatter : formatter { auto format(const explicitly_convertible_to_std_string_view& v, - format_context& ctx) { + format_context& ctx) -> decltype(ctx.out()) { return format_to(ctx.out(), "'{}'", std::string_view(v)); } }; - } // namespace fmt TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) { diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 8a55d890..9944618e 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -276,3 +276,25 @@ std::ostream& operator<<(std::ostream& os, TEST(FormatterTest, FormatExplicitlyConvertibleToStringLikeIgnoreInserter) { EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like())); } + +#ifdef FMT_USE_STRING_VIEW +struct explicitly_convertible_to_std_string_view { + explicit operator fmt::internal::std_string_view() const { + return {"foo", 3u}; + } +}; + +TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) { + EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like())); +} + +std::ostream& operator<<(std::ostream& os, + explicitly_convertible_to_std_string_view) { + return os << "bar"; +} + +TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringViewIgnoreInserter) { + EXPECT_EQ("foo", + fmt::format("{}", explicitly_convertible_to_std_string_view())); +} +#endif // FMT_USE_STRING_VIEW From ae7c50185dacb005ae1660ce5b2980eb12414993 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 10 Dec 2019 20:44:08 -0800 Subject: [PATCH 3/4] Reintroduce sprintf_format for ABI compatibility --- src/format.cc | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/src/format.cc b/src/format.cc index c373db1c..44ba77f0 100644 --- a/src/format.cc +++ b/src/format.cc @@ -8,6 +8,119 @@ #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE +namespace internal { + +template +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about nonliteral format string. + auto snprintf_ptr = FMT_SNPRINTF; + return precision < 0 ? snprintf_ptr(buf, size, format, value) + : snprintf_ptr(buf, size, format, precision, value); +} +struct sprintf_specs { + int precision; + char type; + bool alt : 1; + + template + constexpr sprintf_specs(basic_format_specs specs) + : precision(specs.precision), type(specs.type), alt(specs.alt) {} + + constexpr bool has_precision() const { return precision >= 0; } +}; + +// This is deprecated and is kept only to preserve ABI compatibility. +template +char* sprintf_format(Double value, internal::buffer& buf, + sprintf_specs specs) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buf.capacity() != 0, "empty buffer"); + + // Build format string. + enum { max_format_size = 10 }; // longest format: %#-*.*Lg + char format[max_format_size]; + char* format_ptr = format; + *format_ptr++ = '%'; + if (specs.alt || !specs.type) *format_ptr++ = '#'; + if (specs.precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same::value) *format_ptr++ = 'L'; + + char type = specs.type; + + if (type == '%') + type = 'f'; + else if (type == 0 || type == 'n') + type = 'g'; +#if FMT_MSC_VER + if (type == 'F') { + // MSVC's printf doesn't support 'F'. + type = 'f'; + } +#endif + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + char* start = nullptr; + char* decimal_point_pos = nullptr; + for (;;) { + std::size_t buffer_size = buf.capacity(); + start = &buf[0]; + int result = + format_float(start, buffer_size, format, specs.precision, value); + if (result >= 0) { + unsigned n = internal::to_unsigned(result); + if (n < buf.capacity()) { + // Find the decimal point. + auto p = buf.data(), end = p + n; + if (*p == '+' || *p == '-') ++p; + if (specs.type != 'a' && specs.type != 'A') { + while (p < end && *p >= '0' && *p <= '9') ++p; + if (p < end && *p != 'e' && *p != 'E') { + decimal_point_pos = p; + if (!specs.type) { + // Keep only one trailing zero after the decimal point. + ++p; + if (*p == '0') ++p; + while (p != end && *p >= '1' && *p <= '9') ++p; + char* where = p; + while (p != end && *p == '0') ++p; + if (p == end || *p < '0' || *p > '9') { + if (p != end) std::memmove(where, p, to_unsigned(end - p)); + n -= static_cast(p - where); + } + } + } + } + buf.resize(n); + break; // The buffer is large enough - continue with formatting. + } + buf.reserve(n + 1); + } else { + // If result is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buf.reserve(buf.capacity() + 1); + } + } + return decimal_point_pos; +} +} // namespace internal + +template FMT_API char* internal::sprintf_format(double, internal::buffer&, + sprintf_specs); +template FMT_API char* internal::sprintf_format(long double, + internal::buffer&, + sprintf_specs); + template struct FMT_API internal::basic_data; // Workaround a bug in MSVC2013 that prevents instantiation of format_float. From b7eb8c8921428d2082fdafead0e936181c2232e6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 10 Dec 2019 21:50:14 -0800 Subject: [PATCH 4/4] Prepare for the next release --- ChangeLog.rst | 15 +++++++++++++++ include/fmt/core.h | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ChangeLog.rst b/ChangeLog.rst index dd712418..1c34c602 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,3 +1,18 @@ +6.1.2 - TBD +----------- + +* Fixed ABI compatibility with ``libfmt.so.6.0.0``. + +* Fixed handling types convertible to ``std::string_view`` + (`#1451 `_). + Thanks `@denizevrenci (Deniz Evrenci) `_. + +* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option. + +* Fixed sign conversion warnings + (`#1440 `_). + Thanks `@0x8000-0000 (Florin Iucha) `_. + 6.1.1 - 2019-12-04 ------------------ diff --git a/include/fmt/core.h b/include/fmt/core.h index 398253cb..9fd8df28 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -15,7 +15,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 60101 +#define FMT_VERSION 60102 #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x)