Changed the way the char type is deducted for StringLike, eliminated function overloads in favor of if constexpr

This commit is contained in:
XAMeLeOH 2026-02-22 14:54:43 +01:00
parent c477238842
commit f75b7dfcda
3 changed files with 86 additions and 71 deletions

View File

@ -913,8 +913,10 @@ struct ci_matcher_supported : std::false_type {};
template<>
struct ci_matcher_supported<char> : std::true_type {};
#if GTEST_HAS_STD_WSTRING
template<>
struct ci_matcher_supported<wchar_t> : std::true_type {};
#endif // GTEST_HAS_STD_WSTRING
// String matchers.
@ -965,8 +967,14 @@ class [[nodiscard]] StrEqualityMatcher {
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
const StringType s2(s);
const bool eq = MatchStrings(s2, ci_matcher_supported<typename StringType::value_type>{});
return expect_eq_ == eq;
if constexpr(ci_matcher_supported<typename StringType::value_type>::value) {
const bool eq = CaseSensitive::value ?
s2 == string_ : CaseInsensitiveStringEquals(s2, string_);
return expect_eq_ == eq;
} else {
const bool eq = s2 == string_;
return expect_eq_ == eq;
}
}
void DescribeTo(::std::ostream* os) const {
@ -978,21 +986,10 @@ class [[nodiscard]] StrEqualityMatcher {
}
private:
template <typename S>
bool MatchStrings(const S& s2, std::true_type) const {
return CaseSensitive::value ? s2 == string_
: CaseInsensitiveStringEquals(s2, string_);
}
template <typename S>
bool MatchStrings(const S& s2, std::false_type) const {
return s2 == string_;
}
void DescribeToHelper(bool expect_eq, ::std::ostream* os) const {
*os << (expect_eq ? "is " : "isn't ");
*os << "equal to ";
if (!CaseSensitive::value) {
if constexpr(!CaseSensitive::value) {
*os << "(ignoring case) ";
}
UniversalPrint(string_, os);
@ -4808,7 +4805,7 @@ internal::ResultOfMatcher<Callable, InnerMatcher> ResultOf(
// Matches a string equal to str.
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>>> StrEq(
const internal::StringLike<T>& str) {
return MakePolymorphicMatcher(
@ -4817,7 +4814,7 @@ PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>>> StrEq
// Matches a string not equal to str.
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>>> StrNe(
const internal::StringLike<T>& str) {
return MakePolymorphicMatcher(
@ -4826,7 +4823,7 @@ PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>>> StrNe
// Matches a string equal to str, ignoring case.
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>, std::false_type>> StrCaseEq(
const internal::StringLike<T>& str) {
return MakePolymorphicMatcher(
@ -4836,7 +4833,7 @@ PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>, std::f
// Matches a string not equal to str, ignoring case.
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>, std::false_type>> StrCaseNe(
const internal::StringLike<T>& str) {
return MakePolymorphicMatcher(
@ -4847,7 +4844,7 @@ PolymorphicMatcher<internal::StrEqualityMatcher<std::basic_string<CharT>, std::f
// Creates a matcher that matches any string, std::string, or C string
// that contains the given substring.
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::HasSubstrMatcher<std::basic_string<CharT>>> HasSubstr(
const internal::StringLike<T>& substring) {
return MakePolymorphicMatcher(
@ -4856,7 +4853,7 @@ PolymorphicMatcher<internal::HasSubstrMatcher<std::basic_string<CharT>>> HasSubs
// Matches a string that starts with 'prefix' (case-sensitive).
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::StartsWithMatcher<std::basic_string<CharT>>> StartsWith(
const internal::StringLike<T>& prefix) {
return MakePolymorphicMatcher(
@ -4865,7 +4862,7 @@ PolymorphicMatcher<internal::StartsWithMatcher<std::basic_string<CharT>>> Starts
// Matches a string that ends with 'suffix' (case-sensitive).
template <typename T = std::string,
typename CharT = internal::char_type_traits_t<T>>
typename CharT = internal::get_char_type_t<T>>
PolymorphicMatcher<internal::EndsWithMatcher<std::basic_string<CharT>>> EndsWith(
const internal::StringLike<T>& suffix) {
return MakePolymorphicMatcher(

View File

@ -1364,6 +1364,57 @@ TEST(StrEqTest, AllowsWideChars) {
#endif // GTEST_HAS_STD_WSTRING
}
TEST(StrEqTest, AllowsCustomStringLikeType) {
struct MyString {
operator std::string() const { return std::string("Hello"); }
};
Matcher<const char*> m = StrEq(MyString{});
EXPECT_TRUE(m.Matches("Hello"));
EXPECT_FALSE(m.Matches("hello"));
EXPECT_FALSE(m.Matches(nullptr));
#if GTEST_HAS_STD_WSTRING
struct MyWString {
operator std::wstring() const { return std::wstring(L"Hello"); }
};
Matcher<const wchar_t*> mw = StrEq(MyWString{});
EXPECT_TRUE(mw.Matches(L"Hello"));
EXPECT_FALSE(mw.Matches(L"hello"));
EXPECT_FALSE(mw.Matches(nullptr));
#endif // GTEST_HAS_STD_WSTRING
struct MyU16String {
operator std::u16string() const { return std::u16string(u"Hello"); }
};
Matcher<const char16_t*> m16 = StrEq(MyU16String{});
EXPECT_TRUE(m16.Matches(u"Hello"));
EXPECT_FALSE(m16.Matches(u"hello"));
EXPECT_FALSE(m16.Matches(nullptr));
struct MyU32String {
operator std::u32string() const { return std::u32string(U"Hello"); }
};
Matcher<const char32_t*> m32 = StrEq(MyU32String{});
EXPECT_TRUE(m32.Matches(U"Hello"));
EXPECT_FALSE(m32.Matches(U"hello"));
EXPECT_FALSE(m32.Matches(nullptr));
#ifdef __cpp_lib_char8_t
struct MyU8String {
operator std::u8string() const { return std::u8string(u8"Hello"); }
};
Matcher<const char8_t*> m8 = StrEq(MyU8String{});
EXPECT_TRUE(m8.Matches(u8"Hello"));
EXPECT_FALSE(m8.Matches(u8"hello"));
EXPECT_FALSE(m8.Matches(nullptr));
#endif // __cpp_lib_char8_t
}
TEST(StrNeTest, MatchesUnequalString) {
Matcher<const char*> m = StrNe("Hello");
EXPECT_TRUE(m.Matches(""));

View File

@ -815,64 +815,31 @@ class [[nodiscard]] ImplicitCastEqMatcher {
StoredRhs stored_rhs_;
};
// Backported std::remove_cvref_t (since C++20)
template<typename T>
struct remove_cvref {
using type = std::remove_cv_t<std::remove_reference_t<T>>;
template<typename T, typename CharT>
struct maybe_stringish : std::bool_constant<std::is_constructible_v<std::basic_string<CharT>, T>> {
using type = CharT;
};
struct default_char_type : std::true_type
{
using type = char;
};
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
// Character type detecting traits
// It is used to deduct the CharT (Character Trait) out of
// [const] char/wchar_t/char8_t/char16_t/char32_t *, basic_string<...>, basic_string_view<...>
template <typename T>
struct is_char_type : std::false_type {};
template <> struct is_char_type<char> : std::true_type {};
template <> struct is_char_type<char16_t> : std::true_type {};
template <> struct is_char_type<char32_t> : std::true_type {};
using get_char_type_t = typename std::disjunction<
maybe_stringish<T, char>,
#if GTEST_HAS_STD_WSTRING
template <> struct is_char_type<wchar_t> : std::true_type {};
maybe_stringish<T, wchar_t>,
#endif // GTEST_HAS_STD_WSTRING
maybe_stringish<T, char16_t>,
maybe_stringish<T, char32_t>,
#ifdef __cpp_lib_char8_t
template <> struct is_char_type<char8_t> : std::true_type {};
maybe_stringish<T, char8_t>,
#endif // __cpp_lib_char8_t
default_char_type // we will default to char anyway
>::type;
template <typename T>
struct type_identity { using type = T; };
template <typename T, typename = void>
struct char_type_traits_impl {};
template <typename CharT, typename Traits, typename Alloc>
struct char_type_traits_impl<std::basic_string<CharT, Traits, Alloc>,
std::void_t<std::enable_if_t<is_char_type<std::remove_cv_t<CharT>>::value>>> :
type_identity<CharT> {};
template <typename CharT, typename Traits>
struct char_type_traits_impl<std::basic_string_view<CharT, Traits>,
std::void_t<std::enable_if_t<is_char_type<std::remove_cv_t<CharT>>::value>>> :
type_identity<CharT> {};
template <typename CharT>
struct char_type_traits_impl<CharT*,
std::void_t<std::enable_if_t<is_char_type<std::remove_cv_t<CharT>>::value>>> :
type_identity<std::remove_cv_t<CharT>> {};
template <typename T>
struct char_type_traits : char_type_traits_impl<std::decay_t<remove_cvref_t<T>>> {};
template<typename T>
using char_type_traits_t = typename char_type_traits<T>::type;
template <typename T,
typename = std::enable_if_t<
std::is_constructible_v<std::basic_string<char_type_traits_t<T>>, T>>>
template<typename T, typename = std::enable_if_t<std::is_constructible_v<std::basic_string<get_char_type_t<T>>, T>>>
using StringLike = T;
// Implements polymorphic matchers MatchesRegex(regex) and