diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index 1c15e8242..dbbe6f455 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -913,8 +913,10 @@ struct ci_matcher_supported : std::false_type {}; template<> struct ci_matcher_supported : std::true_type {}; +#if GTEST_HAS_STD_WSTRING template<> struct ci_matcher_supported : 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{}); - return expect_eq_ == eq; + if constexpr(ci_matcher_supported::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 - bool MatchStrings(const S& s2, std::true_type) const { - return CaseSensitive::value ? s2 == string_ - : CaseInsensitiveStringEquals(s2, string_); - } - - template - 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 ResultOf( // Matches a string equal to str. template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher>> StrEq( const internal::StringLike& str) { return MakePolymorphicMatcher( @@ -4817,7 +4814,7 @@ PolymorphicMatcher>> StrEq // Matches a string not equal to str. template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher>> StrNe( const internal::StringLike& str) { return MakePolymorphicMatcher( @@ -4826,7 +4823,7 @@ PolymorphicMatcher>> StrNe // Matches a string equal to str, ignoring case. template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher, std::false_type>> StrCaseEq( const internal::StringLike& str) { return MakePolymorphicMatcher( @@ -4836,7 +4833,7 @@ PolymorphicMatcher, std::f // Matches a string not equal to str, ignoring case. template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher, std::false_type>> StrCaseNe( const internal::StringLike& str) { return MakePolymorphicMatcher( @@ -4847,7 +4844,7 @@ PolymorphicMatcher, std::f // Creates a matcher that matches any string, std::string, or C string // that contains the given substring. template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher>> HasSubstr( const internal::StringLike& substring) { return MakePolymorphicMatcher( @@ -4856,7 +4853,7 @@ PolymorphicMatcher>> HasSubs // Matches a string that starts with 'prefix' (case-sensitive). template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher>> StartsWith( const internal::StringLike& prefix) { return MakePolymorphicMatcher( @@ -4865,7 +4862,7 @@ PolymorphicMatcher>> Starts // Matches a string that ends with 'suffix' (case-sensitive). template > + typename CharT = internal::get_char_type_t> PolymorphicMatcher>> EndsWith( const internal::StringLike& suffix) { return MakePolymorphicMatcher( diff --git a/googlemock/test/gmock-matchers-comparisons_test.cc b/googlemock/test/gmock-matchers-comparisons_test.cc index a94febf4e..d7888cb62 100644 --- a/googlemock/test/gmock-matchers-comparisons_test.cc +++ b/googlemock/test/gmock-matchers-comparisons_test.cc @@ -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 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 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 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 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 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 m = StrNe("Hello"); EXPECT_TRUE(m.Matches("")); diff --git a/googletest/include/gtest/gtest-matchers.h b/googletest/include/gtest/gtest-matchers.h index b0e61289f..b39216282 100644 --- a/googletest/include/gtest/gtest-matchers.h +++ b/googletest/include/gtest/gtest-matchers.h @@ -815,64 +815,31 @@ class [[nodiscard]] ImplicitCastEqMatcher { StoredRhs stored_rhs_; }; -// Backported std::remove_cvref_t (since C++20) -template -struct remove_cvref { - using type = std::remove_cv_t>; +template +struct maybe_stringish : std::bool_constant, T>> { + using type = CharT; +}; + +struct default_char_type : std::true_type +{ + using type = char; }; template -using remove_cvref_t = typename remove_cvref::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 -struct is_char_type : std::false_type {}; - -template <> struct is_char_type : std::true_type {}; -template <> struct is_char_type : std::true_type {}; -template <> struct is_char_type : std::true_type {}; - +using get_char_type_t = typename std::disjunction< + maybe_stringish, #if GTEST_HAS_STD_WSTRING -template <> struct is_char_type : std::true_type {}; + maybe_stringish, #endif // GTEST_HAS_STD_WSTRING - + maybe_stringish, + maybe_stringish, #ifdef __cpp_lib_char8_t -template <> struct is_char_type : std::true_type {}; + maybe_stringish, #endif // __cpp_lib_char8_t + default_char_type // we will default to char anyway + >::type; -template -struct type_identity { using type = T; }; - -template -struct char_type_traits_impl {}; - -template -struct char_type_traits_impl, - std::void_t>::value>>> : - type_identity {}; - -template -struct char_type_traits_impl, - std::void_t>::value>>> : - type_identity {}; - -template -struct char_type_traits_impl>::value>>> : - type_identity> {}; - -template -struct char_type_traits : char_type_traits_impl>> {}; - -template -using char_type_traits_t = typename char_type_traits::type; - -template >, T>>> +template>, T>>> using StringLike = T; // Implements polymorphic matchers MatchesRegex(regex) and