Cleanup string traits

This commit is contained in:
Victor Zverovich 2024-01-05 17:48:12 -08:00
parent 1e938dda20
commit 0641b844ac
6 changed files with 36 additions and 54 deletions

View File

@ -413,17 +413,17 @@ FMT_CONSTEXPR auto to_unsigned(Int value) ->
} }
// A heuristic to detect std::string and std::[experimental::]string_view. // A heuristic to detect std::string and std::[experimental::]string_view.
// It is mainly used to avoid dependency on <[experimental/]string_view>.
template <typename T, typename Enable = void> template <typename T, typename Enable = void>
struct is_string_like : std::false_type {}; struct is_std_string_like : std::false_type {};
template <typename T> template <typename T>
struct is_string_like<T, void_t<decltype(std::declval<T>().find_first_of( struct is_std_string_like<T, void_t<decltype(std::declval<T>().find_first_of(
typename T::value_type(), 0))>> : std::true_type { typename T::value_type(), 0))>>
}; : std::true_type {};
FMT_CONSTEXPR inline auto is_utf8() -> bool { FMT_CONSTEXPR inline auto is_utf8() -> bool {
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7";
// Avoid an MSVC sign extension bug: https://github.com/fmtlib/fmt/pull/2297.
// Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297).
using uchar = unsigned char; using uchar = unsigned char;
return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 &&
uchar(section[1]) == 0xA7); uchar(section[1]) == 0xA7);
@ -495,7 +495,7 @@ template <typename Char> class basic_string_view {
``std::basic_string_view`` object. ``std::basic_string_view`` object.
*/ */
template <typename S, template <typename S,
FMT_ENABLE_IF(detail::is_string_like<S>::value&& std::is_same< FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
typename S::value_type, Char>::value)> typename S::value_type, Char>::value)>
FMT_CONSTEXPR basic_string_view(const S& s) noexcept FMT_CONSTEXPR basic_string_view(const S& s) noexcept
: data_(s.data()), size_(s.size()) {} : data_(s.data()), size_(s.size()) {}
@ -576,9 +576,9 @@ template <typename Char, FMT_ENABLE_IF(is_char<Char>::value)>
FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> { FMT_INLINE auto to_string_view(const Char* s) -> basic_string_view<Char> {
return s; return s;
} }
template <typename S, FMT_ENABLE_IF(is_string_like<S>::value)> template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
inline auto to_string_view(const S& s) inline auto to_string_view(const T& s)
-> basic_string_view<typename S::value_type> { -> basic_string_view<typename T::value_type> {
return s; return s;
} }
template <typename Char> template <typename Char>
@ -586,15 +586,14 @@ constexpr auto to_string_view(basic_string_view<Char> s)
-> basic_string_view<Char> { -> basic_string_view<Char> {
return s; return s;
} }
void to_string_view(...);
// Specifies whether S is a string type convertible to fmt::basic_string_view. template <typename T, typename Enable = void>
// It should be a constexpr function but MSVC 2017 fails to compile it in struct has_to_string_view : std::false_type {};
// enable_if and MSVC 2015 fails to compile it as an alias template. // detail:: is intentional since to_string_view is not an extension point.
// ADL is intentionally disabled as to_string_view is not an extension point. template <typename T>
template <typename S, typename = void> struct has_to_string_view<
struct is_string T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
: std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {}; : std::true_type {};
enum class type { enum class type {
none_type, none_type,
@ -1482,7 +1481,7 @@ template <typename Context> struct arg_mapper {
template <typename T, typename U = remove_const_t<T>, template <typename T, typename U = remove_const_t<T>,
FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value || FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<U>::value ||
std::is_union<U>::value) && std::is_union<U>::value) &&
!is_string<U>::value && !is_char<U>::value && !has_to_string_view<U>::value && !is_char<U>::value &&
!is_named_arg<U>::value && !is_named_arg<U>::value &&
!std::is_arithmetic<format_as_t<U>>::value)> !std::is_arithmetic<format_as_t<U>>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(T& val) FMT_CONSTEXPR FMT_INLINE auto map(T& val)

View File

@ -3733,7 +3733,7 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> value)
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_string<T>::value)> FMT_ENABLE_IF(has_to_string_view<T>::value)>
constexpr auto write(OutputIt out, const T& value) -> OutputIt { constexpr auto write(OutputIt out, const T& value) -> OutputIt {
return write<Char>(out, to_string_view(value)); return write<Char>(out, to_string_view(value));
} }
@ -3786,7 +3786,7 @@ auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {},
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
typename Context = basic_format_context<OutputIt, Char>> typename Context = basic_format_context<OutputIt, Char>>
FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t<
std::is_class<T>::value && !is_string<T>::value && std::is_class<T>::value && !has_to_string_view<T>::value &&
!is_floating_point<T>::value && !std::is_same<T, Char>::value && !is_floating_point<T>::value && !std::is_same<T, Char>::value &&
!std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map( !std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
value))>>::value, value))>>::value,

View File

@ -605,8 +605,7 @@ inline auto vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
template <typename S, typename... T, template <typename S, typename... T, typename Char = char_t<S>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt), return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));

View File

@ -41,23 +41,6 @@ template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
return out; return out;
} }
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static constexpr const bool value =
is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map { template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type; template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...); template <typename> static void check(...);
@ -377,7 +360,8 @@ struct formatter<Tuple, Char,
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static constexpr const bool value = static constexpr const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && detail::is_range_<T>::value &&
!detail::has_to_string_view<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_convertible<T, detail::std_string_view<Char>>::value; !std::is_convertible<T, detail::std_string_view<Char>>::value;
}; };

View File

@ -293,7 +293,7 @@ template <typename T, typename C> class is_variant_formattable_ {
template <typename Char, typename OutputIt, typename T> template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value) if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v)); return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>) else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v); return write_escaped_char(out, v);

View File

@ -32,27 +32,27 @@ using testing::Contains;
struct non_string {}; struct non_string {};
template <typename T> class is_string_test : public testing::Test {}; template <typename T> class has_to_string_view_test : public testing::Test {};
using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>; using string_char_types = testing::Types<char, wchar_t, char16_t, char32_t>;
TYPED_TEST_SUITE(is_string_test, string_char_types); TYPED_TEST_SUITE(has_to_string_view_test, string_char_types);
template <typename Char> template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {}; struct derived_from_string_view : fmt::basic_string_view<Char> {};
TYPED_TEST(is_string_test, is_string) { TYPED_TEST(has_to_string_view_test, has_to_string_view) {
EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<const TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value); EXPECT_TRUE(fmt::detail::has_to_string_view<fmt::basic_string_view<TypeParam>>::value);
EXPECT_TRUE( EXPECT_TRUE(
fmt::detail::is_string<derived_from_string_view<TypeParam>>::value); fmt::detail::has_to_string_view<derived_from_string_view<TypeParam>>::value);
using fmt_string_view = fmt::detail::std_string_view<TypeParam>; using fmt_string_view = fmt::detail::std_string_view<TypeParam>;
EXPECT_TRUE(std::is_empty<fmt_string_view>::value != EXPECT_TRUE(std::is_empty<fmt_string_view>::value !=
fmt::detail::is_string<fmt_string_view>::value); fmt::detail::has_to_string_view<fmt_string_view>::value);
EXPECT_FALSE(fmt::detail::is_string<non_string>::value); EXPECT_FALSE(fmt::detail::has_to_string_view<non_string>::value);
} }
// std::is_constructible is broken in MSVC until version 2015. // std::is_constructible is broken in MSVC until version 2015.