diff --git a/include/fmt/core.h b/include/fmt/core.h index 4a83cdaf..0efe7fce 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -413,17 +413,17 @@ FMT_CONSTEXPR auto to_unsigned(Int value) -> } // A heuristic to detect std::string and std::[experimental::]string_view. +// It is mainly used to avoid dependency on <[experimental/]string_view>. template -struct is_string_like : std::false_type {}; +struct is_std_string_like : std::false_type {}; template -struct is_string_like().find_first_of( - typename T::value_type(), 0))>> : std::true_type { -}; +struct is_std_string_like().find_first_of( + typename T::value_type(), 0))>> + : std::true_type {}; FMT_CONSTEXPR inline auto is_utf8() -> bool { FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; - - // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). + // Avoid an MSVC sign extension bug: https://github.com/fmtlib/fmt/pull/2297. using uchar = unsigned char; return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && uchar(section[1]) == 0xA7); @@ -495,7 +495,7 @@ template class basic_string_view { ``std::basic_string_view`` object. */ template ::value&& std::is_same< + FMT_ENABLE_IF(detail::is_std_string_like::value&& std::is_same< typename S::value_type, Char>::value)> FMT_CONSTEXPR basic_string_view(const S& s) noexcept : data_(s.data()), size_(s.size()) {} @@ -576,9 +576,9 @@ template ::value)> FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view { return s; } -template ::value)> -inline auto to_string_view(const S& s) - -> basic_string_view { +template ::value)> +inline auto to_string_view(const T& s) + -> basic_string_view { return s; } template @@ -586,15 +586,14 @@ constexpr auto to_string_view(basic_string_view s) -> basic_string_view { return s; } -void to_string_view(...); -// Specifies whether S is a string type convertible to fmt::basic_string_view. -// It should be a constexpr function but MSVC 2017 fails to compile it in -// enable_if and MSVC 2015 fails to compile it as an alias template. -// ADL is intentionally disabled as to_string_view is not an extension point. -template -struct is_string - : std::is_class()))> {}; +template +struct has_to_string_view : std::false_type {}; +// detail:: is intentional since to_string_view is not an extension point. +template +struct has_to_string_view< + T, void_t()))>> + : std::true_type {}; enum class type { none_type, @@ -1482,7 +1481,7 @@ template struct arg_mapper { template , FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || std::is_union::value) && - !is_string::value && !is_char::value && + !has_to_string_view::value && !is_char::value && !is_named_arg::value && !std::is_arithmetic>::value)> FMT_CONSTEXPR FMT_INLINE auto map(T& val) diff --git a/include/fmt/format.h b/include/fmt/format.h index 20db4ce2..f36b0240 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3733,7 +3733,7 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) } template ::value)> + FMT_ENABLE_IF(has_to_string_view::value)> constexpr auto write(OutputIt out, const T& value) -> OutputIt { return write(out, to_string_view(value)); } @@ -3786,7 +3786,7 @@ auto write(OutputIt out, const T* value, const format_specs& specs = {}, template > FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< - std::is_class::value && !is_string::value && + std::is_class::value && !has_to_string_view::value && !is_floating_point::value && !std::is_same::value && !std::is_same().map( value))>>::value, diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 35445abc..b270dcf4 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -605,8 +605,7 @@ inline auto vsprintf( std::string message = fmt::sprintf("The answer is %d", 42); \endrst */ -template ::value, char_t>> +template > inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { return vsprintf(detail::to_string_view(fmt), fmt::make_format_args>(args...)); diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index a9cd60e5..182458bf 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -41,23 +41,6 @@ template auto copy(wchar_t ch, OutputIt out) -> OutputIt { return out; } -// Returns true if T has a std::string-like interface, like std::string_view. -template class is_std_string_like { - template - static auto check(U* p) - -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); - template static void check(...); - - public: - static constexpr const bool value = - is_string::value || - std::is_convertible>::value || - !std::is_void(nullptr))>::value; -}; - -template -struct is_std_string_like> : std::true_type {}; - template class is_map { template static auto check(U*) -> typename U::mapped_type; template static void check(...); @@ -377,7 +360,8 @@ struct formatter struct is_range { static constexpr const bool value = - detail::is_range_::value && !detail::is_std_string_like::value && + detail::is_range_::value && + !detail::has_to_string_view::value && !std::is_convertible>::value && !std::is_convertible>::value; }; diff --git a/include/fmt/std.h b/include/fmt/std.h index 7cff1159..885176c0 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -293,7 +293,7 @@ template class is_variant_formattable_ { template auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { - if constexpr (is_string::value) + if constexpr (has_to_string_view::value) return write_escaped_string(out, detail::to_string_view(v)); else if constexpr (std::is_same_v) return write_escaped_char(out, v); diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 7f33fb27..c5482dc1 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -32,27 +32,27 @@ using testing::Contains; struct non_string {}; -template class is_string_test : public testing::Test {}; +template class has_to_string_view_test : public testing::Test {}; using string_char_types = testing::Types; -TYPED_TEST_SUITE(is_string_test, string_char_types); +TYPED_TEST_SUITE(has_to_string_view_test, string_char_types); template struct derived_from_string_view : fmt::basic_string_view {}; -TYPED_TEST(is_string_test, is_string) { - EXPECT_TRUE(fmt::detail::is_string::value); - EXPECT_TRUE(fmt::detail::is_string::value); - EXPECT_TRUE(fmt::detail::is_string::value); - EXPECT_TRUE(fmt::detail::is_string::value); - EXPECT_TRUE(fmt::detail::is_string>::value); - EXPECT_TRUE(fmt::detail::is_string>::value); +TYPED_TEST(has_to_string_view_test, has_to_string_view) { + EXPECT_TRUE(fmt::detail::has_to_string_view::value); + EXPECT_TRUE(fmt::detail::has_to_string_view::value); + EXPECT_TRUE(fmt::detail::has_to_string_view::value); + EXPECT_TRUE(fmt::detail::has_to_string_view::value); + EXPECT_TRUE(fmt::detail::has_to_string_view>::value); + EXPECT_TRUE(fmt::detail::has_to_string_view>::value); EXPECT_TRUE( - fmt::detail::is_string>::value); + fmt::detail::has_to_string_view>::value); using fmt_string_view = fmt::detail::std_string_view; EXPECT_TRUE(std::is_empty::value != - fmt::detail::is_string::value); - EXPECT_FALSE(fmt::detail::is_string::value); + fmt::detail::has_to_string_view::value); + EXPECT_FALSE(fmt::detail::has_to_string_view::value); } // std::is_constructible is broken in MSVC until version 2015.