This commit is contained in:
Dominik Kaszewski 2025-02-18 12:47:41 +05:30 committed by GitHub
commit 1f6ccd6c22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 104 additions and 24 deletions

View File

@ -209,6 +209,15 @@ contract of the function.
| `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. | | `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. |
| `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. | | `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. |
| `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. | | `WhenDynamicCastTo<T>(m)` | when `argument` is passed through `dynamic_cast<T>()`, it matches matcher `m`. |
| `WhenStaticCastTo<T>(m)` | when `argument` is passed through `static_cast<T>()`, it matches matcher `m`. |
`WhenDynamicCast` can be used for safely checking the dynamic type of an object
and navigating the inheritance tree of an object.
`WhenStaticCast` is primarily used to check and argument which was type-erased
as `void*`. It can also be used as an unsafe replacement of `WhenDynamicCast`
in environemnts without RTTI, or for pointer-based type punning by chaining
a cast to `void*` and then another pointer (equivalent to `reinterpret_cast`).
## Multi-argument Matchers {#MultiArgMatchers} ## Multi-argument Matchers {#MultiArgMatchers}

View File

@ -2022,17 +2022,10 @@ class PointerMatcher {
const InnerMatcher matcher_; const InnerMatcher matcher_;
}; };
#if GTEST_HAS_RTTI template <typename To, typename Caster>
// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or class WhenCastToMatcherBase {
// reference that matches inner_matcher when dynamic_cast<T> is applied.
// The result of dynamic_cast<To> is forwarded to the inner matcher.
// If To is a pointer and the cast fails, the inner matcher will receive NULL.
// If To is a reference and the cast fails, this matcher returns false
// immediately.
template <typename To>
class WhenDynamicCastToMatcherBase {
public: public:
explicit WhenDynamicCastToMatcherBase(const Matcher<To>& matcher) explicit WhenCastToMatcherBase(const Matcher<To>& matcher)
: matcher_(matcher) {} : matcher_(matcher) {}
void DescribeTo(::std::ostream* os) const { void DescribeTo(::std::ostream* os) const {
@ -2045,6 +2038,12 @@ class WhenDynamicCastToMatcherBase {
matcher_.DescribeNegationTo(os); matcher_.DescribeNegationTo(os);
} }
template <typename From>
bool MatchAndExplain(From&& from, MatchResultListener* listener) const {
decltype(auto) to = Caster::template Cast<To>(from);
return MatchPrintAndExplain(to, this->matcher_, listener);
}
protected: protected:
const Matcher<To> matcher_; const Matcher<To> matcher_;
@ -2052,32 +2051,40 @@ class WhenDynamicCastToMatcherBase {
private: private:
static void GetCastTypeDescription(::std::ostream* os) { static void GetCastTypeDescription(::std::ostream* os) {
*os << "when dynamic_cast to " << GetToName() << ", "; *os << "when " << Caster::Name << " to " << GetToName() << ", ";
} }
}; };
// Primary template. #if GTEST_HAS_RTTI
// To is a pointer. Cast and forward the result. class DynamicCaster {
template <typename To>
class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase<To> {
public: public:
explicit WhenDynamicCastToMatcher(const Matcher<To>& matcher) static constexpr const char* Name = "dynamic_cast";
: WhenDynamicCastToMatcherBase<To>(matcher) {}
template <typename From> template <typename To, typename From>
bool MatchAndExplain(From from, MatchResultListener* listener) const { static To Cast(From&& from) {
To to = dynamic_cast<To>(from); return dynamic_cast<To>(from);
return MatchPrintAndExplain(to, this->matcher_, listener);
} }
}; };
// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or
// reference that matches inner_matcher when dynamic_cast<T> is applied.
// The result of dynamic_cast<To> is forwarded to the inner matcher.
// To is a pointer. Cast and forward the result, which might be nullptr.
template <typename To>
class WhenDynamicCastToMatcher
: public WhenCastToMatcherBase<To, DynamicCaster> {
public:
using WhenCastToMatcherBase<To, DynamicCaster>::WhenCastToMatcherBase;
};
// Specialize for references. // Specialize for references.
// In this case we return false if the dynamic_cast fails. // In this case we return false if the dynamic_cast fails.
template <typename To> template <typename To>
class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> { class WhenDynamicCastToMatcher<To&>
: public WhenCastToMatcherBase<To&, DynamicCaster> {
public: public:
explicit WhenDynamicCastToMatcher(const Matcher<To&>& matcher) using WhenCastToMatcherBase<To&, DynamicCaster>::WhenCastToMatcherBase;
: WhenDynamicCastToMatcherBase<To&>(matcher) {}
template <typename From> template <typename From>
bool MatchAndExplain(From& from, MatchResultListener* listener) const { bool MatchAndExplain(From& from, MatchResultListener* listener) const {
@ -2092,6 +2099,25 @@ class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
}; };
#endif // GTEST_HAS_RTTI #endif // GTEST_HAS_RTTI
// Implements the WhenStaticCastTo<T>(m) matcher that matches a pointer or
// reference that matches inner_matcher when static_cast<T> is applied.
// The result of static_cast<To> is forwarded to the inner matcher.
class StaticCaster {
public:
static constexpr const char* Name = "static_cast";
template <typename To, typename From>
static To Cast(From&& from) {
return static_cast<To>(from);
}
};
template <typename To>
class WhenStaticCastToMatcher : public WhenCastToMatcherBase<To, StaticCaster> {
public:
using WhenCastToMatcherBase<To, StaticCaster>::WhenCastToMatcherBase;
};
// Implements the Field() matcher for matching a field (i.e. member // Implements the Field() matcher for matching a field (i.e. member
// variable) of an object. // variable) of an object.
template <typename Class, typename FieldType> template <typename Class, typename FieldType>
@ -4424,6 +4450,16 @@ WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
} }
#endif // GTEST_HAS_RTTI #endif // GTEST_HAS_RTTI
// Creates a matcher that matches a pointer or reference that matches
// inner_matcher when static_cast<To> is applied.
// The result of static_cast<To> is forwarded to the inner matcher.
template <typename To>
inline PolymorphicMatcher<internal::WhenStaticCastToMatcher<To>>
WhenStaticCastTo(const Matcher<To>& inner_matcher) {
return MakePolymorphicMatcher(
internal::WhenStaticCastToMatcher<To>(inner_matcher));
}
// Creates a matcher that matches an object whose given field matches // Creates a matcher that matches an object whose given field matches
// 'matcher'. For example, // 'matcher'. For example,
// Field(&Foo::number, Ge(5)) // Field(&Foo::number, Ge(5))

View File

@ -2333,6 +2333,41 @@ TEST(WhenDynamicCastToTest, BadReference) {
} }
#endif // GTEST_HAS_RTTI #endif // GTEST_HAS_RTTI
TEST(WhenStaticCastToTest, VoidPointer) {
Derived derived;
derived.i = 4;
void* as_void_ptr = &derived;
EXPECT_THAT(as_void_ptr, WhenStaticCastTo<Derived*>(Pointee(FieldIIs(4))));
EXPECT_THAT(as_void_ptr,
WhenStaticCastTo<Derived*>(Pointee(Not(FieldIIs(5)))));
}
TEST(WhenStaticCastToTest, Inheritance) {
Derived derived;
EXPECT_THAT(derived, WhenStaticCastTo<const Base&>(_));
EXPECT_THAT(&derived, WhenStaticCastTo<Base*>(_));
EXPECT_THAT(derived, WhenStaticCastTo<const Derived&>(_));
EXPECT_THAT(&derived, WhenStaticCastTo<Derived*>(_));
// These will not compile because direct sidecasts are invalid
// EXPECT_THAT(derived, WhenStaticCastTo<const OtherDerived&>(_));
// EXPECT_THAT(&derived, WhenStaticCastTo<OtherDerived*>(_));
Base& as_base_ref = derived;
EXPECT_THAT(as_base_ref, WhenStaticCastTo<const Base&>(_));
EXPECT_THAT(&as_base_ref, WhenStaticCastTo<Base*>(_));
EXPECT_THAT(as_base_ref, WhenStaticCastTo<const Derived&>(_));
EXPECT_THAT(&as_base_ref, WhenStaticCastTo<Derived*>(_));
// These will compile and match, but are not safe
EXPECT_THAT(as_base_ref, WhenStaticCastTo<const OtherDerived&>(_));
EXPECT_THAT(&as_base_ref, WhenStaticCastTo<OtherDerived*>(_));
}
TEST(WhenStaticCastToTest, Punning) {
std::uint32_t u32 = 0xCCCCCCCC;
EXPECT_THAT(&u32, WhenStaticCastTo<void*>(
WhenStaticCastTo<std::uint8_t*>(Pointee(Eq(0xCC)))));
}
class DivisibleByImpl { class DivisibleByImpl {
public: public:
explicit DivisibleByImpl(int a_divider) : divider_(a_divider) {} explicit DivisibleByImpl(int a_divider) : divider_(a_divider) {}