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.
// It is mainly used to avoid dependency on <[experimental/]string_view>.
template <typename T, typename Enable = void>
struct is_string_like : std::false_type {};
struct is_std_string_like : std::false_type {};
template <typename T>
struct is_string_like<T, void_t<decltype(std::declval<T>().find_first_of(
typename T::value_type(), 0))>> : std::true_type {
};
struct is_std_string_like<T, void_t<decltype(std::declval<T>().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 <typename Char> class basic_string_view {
``std::basic_string_view`` object.
*/
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)>
FMT_CONSTEXPR basic_string_view(const S& s) noexcept
: 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> {
return s;
}
template <typename S, FMT_ENABLE_IF(is_string_like<S>::value)>
inline auto to_string_view(const S& s)
-> basic_string_view<typename S::value_type> {
template <typename T, FMT_ENABLE_IF(is_std_string_like<T>::value)>
inline auto to_string_view(const T& s)
-> basic_string_view<typename T::value_type> {
return s;
}
template <typename Char>
@ -586,15 +586,14 @@ constexpr auto to_string_view(basic_string_view<Char> s)
-> basic_string_view<Char> {
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 <typename S, typename = void>
struct is_string
: std::is_class<decltype(detail::to_string_view(std::declval<S>()))> {};
template <typename T, typename Enable = void>
struct has_to_string_view : std::false_type {};
// detail:: is intentional since to_string_view is not an extension point.
template <typename T>
struct has_to_string_view<
T, void_t<decltype(detail::to_string_view(std::declval<T>()))>>
: std::true_type {};
enum class type {
none_type,
@ -1482,7 +1481,7 @@ template <typename Context> struct arg_mapper {
template <typename T, typename U = remove_const_t<T>,
FMT_ENABLE_IF((std::is_class<U>::value || std::is_enum<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 &&
!std::is_arithmetic<format_as_t<U>>::value)>
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,
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 {
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,
typename Context = basic_format_context<OutputIt, Char>>
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 &&
!std::is_same<T, remove_cvref_t<decltype(arg_mapper<Context>().map(
value))>>::value,

View File

@ -605,8 +605,7 @@ inline auto vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
template <typename S, typename... T, typename Char = char_t<S>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt),
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;
}
// 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 U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
@ -377,7 +360,8 @@ struct formatter<Tuple, Char,
template <typename T, typename Char> struct is_range {
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, 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>
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));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);

View File

@ -32,27 +32,27 @@ using testing::Contains;
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>;
TYPED_TEST_SUITE(is_string_test, string_char_types);
TYPED_TEST_SUITE(has_to_string_view_test, string_char_types);
template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {};
TYPED_TEST(is_string_test, is_string) {
EXPECT_TRUE(fmt::detail::is_string<TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<TypeParam>>::value);
TYPED_TEST(has_to_string_view_test, has_to_string_view) {
EXPECT_TRUE(fmt::detail::has_to_string_view<TypeParam*>::value);
EXPECT_TRUE(fmt::detail::has_to_string_view<const TypeParam*>::value);
EXPECT_TRUE(fmt::detail::has_to_string_view<TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::has_to_string_view<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::has_to_string_view<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::has_to_string_view<fmt::basic_string_view<TypeParam>>::value);
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>;
EXPECT_TRUE(std::is_empty<fmt_string_view>::value !=
fmt::detail::is_string<fmt_string_view>::value);
EXPECT_FALSE(fmt::detail::is_string<non_string>::value);
fmt::detail::has_to_string_view<fmt_string_view>::value);
EXPECT_FALSE(fmt::detail::has_to_string_view<non_string>::value);
}
// std::is_constructible is broken in MSVC until version 2015.