Deduce value type in container matchers

Avoid directly using the member `value_type` of a container, and deduce
the value type from the iterator instead (using `std::iterator_traits`).
This allows to use container matchers on containers that do not expose
their value type as a member type.
This commit is contained in:
Martino Pilia 2023-09-24 16:36:56 +02:00
parent e40661d89b
commit 994965666a
No known key found for this signature in database
GPG key ID: B3559EAE43F1A69A
4 changed files with 111 additions and 19 deletions

View file

@ -2404,8 +2404,7 @@ class BeginEndDistanceIsMatcher {
// elements in the containers (which don't properly matter to sets, but can
// occur if the containers are vectors or lists, for example).
//
// Uses the container's const_iterator, value_type, operator ==,
// begin(), and end().
// Uses the container's const_iterator, operator ==, begin(), and end().
template <typename Container>
class ContainerEqMatcher {
public:
@ -2492,6 +2491,11 @@ struct LessComparator {
}
};
// Trait to deduce the value type of a container
template <typename Container>
using ValueType = typename std::iterator_traits<
decltype(std::declval<Container>().begin())>::value_type;
// Implements WhenSortedBy(comparator, container_matcher).
template <typename Comparator, typename ContainerMatcher>
class WhenSortedByMatcher {
@ -2516,8 +2520,7 @@ class WhenSortedByMatcher {
// Transforms std::pair<const Key, Value> into std::pair<Key, Value>
// so that we can match associative containers.
typedef
typename RemoveConstFromKey<typename LhsStlContainer::value_type>::type
LhsValue;
typename RemoveConstFromKey<ValueType<LhsStlContainer>>::type LhsValue;
Impl(const Comparator& comparator, const ContainerMatcher& matcher)
: comparator_(comparator), matcher_(matcher) {}
@ -2583,7 +2586,7 @@ class PointwiseMatcher {
public:
typedef internal::StlContainerView<RhsContainer> RhsView;
typedef typename RhsView::type RhsStlContainer;
typedef typename RhsStlContainer::value_type RhsValue;
typedef ValueType<RhsStlContainer> RhsValue;
static_assert(!std::is_const<RhsContainer>::value,
"RhsContainer type must not be const");
@ -2613,7 +2616,7 @@ class PointwiseMatcher {
LhsView;
typedef typename LhsView::type LhsStlContainer;
typedef typename LhsView::const_reference LhsStlContainerReference;
typedef typename LhsStlContainer::value_type LhsValue;
typedef ValueType<LhsStlContainer> LhsValue;
// We pass the LHS value and the RHS value to the inner matcher by
// reference, as they may be expensive to copy. We must use tuple
// instead of pair here, as a pair cannot hold references (C++ 98,
@ -2699,7 +2702,7 @@ class QuantifierMatcherImpl : public MatcherInterface<Container> {
typedef StlContainerView<RawContainer> View;
typedef typename View::type StlContainer;
typedef typename View::const_reference StlContainerReference;
typedef typename StlContainer::value_type Element;
typedef ValueType<StlContainer> Element;
template <typename InnerMatcher>
explicit QuantifierMatcherImpl(InnerMatcher inner_matcher)
@ -3370,7 +3373,7 @@ class ElementsAreMatcherImpl : public MatcherInterface<Container> {
typedef internal::StlContainerView<RawContainer> View;
typedef typename View::type StlContainer;
typedef typename View::const_reference StlContainerReference;
typedef typename StlContainer::value_type Element;
typedef ValueType<StlContainer> Element;
// Constructs the matcher from a sequence of element values or
// element matchers.
@ -3616,7 +3619,7 @@ class UnorderedElementsAreMatcherImpl
typedef internal::StlContainerView<RawContainer> View;
typedef typename View::type StlContainer;
typedef typename View::const_reference StlContainerReference;
typedef typename StlContainer::value_type Element;
typedef ValueType<StlContainer> Element;
template <typename InputIter>
UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags,
@ -3705,7 +3708,7 @@ class UnorderedElementsAreMatcher {
operator Matcher<Container>() const {
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
typedef typename internal::StlContainerView<RawContainer>::type View;
typedef typename View::value_type Element;
typedef ValueType<View> Element;
typedef ::std::vector<Matcher<const Element&>> MatcherVec;
MatcherVec matchers;
matchers.reserve(::std::tuple_size<MatcherTuple>::value);
@ -3736,7 +3739,7 @@ class ElementsAreMatcher {
typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
typedef typename internal::StlContainerView<RawContainer>::type View;
typedef typename View::value_type Element;
typedef ValueType<View> Element;
typedef ::std::vector<Matcher<const Element&>> MatcherVec;
MatcherVec matchers;
matchers.reserve(::std::tuple_size<MatcherTuple>::value);
@ -4215,7 +4218,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> UnorderedElementsAreArray(
template <typename Container>
inline internal::UnorderedElementsAreArrayMatcher<
typename Container::value_type>
internal::ValueType<Container>>
UnorderedElementsAreArray(const Container& container) {
return UnorderedElementsAreArray(container.begin(), container.end());
}
@ -4761,15 +4764,15 @@ template <typename Tuple2Matcher, typename RhsContainer>
inline internal::UnorderedElementsAreArrayMatcher<
typename internal::BoundSecondMatcher<
Tuple2Matcher,
typename internal::StlContainerView<
typename std::remove_const<RhsContainer>::type>::type::value_type>>
internal::ValueType<typename internal::StlContainerView<
typename std::remove_const<RhsContainer>::type>::type>>>
UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
const RhsContainer& rhs_container) {
// RhsView allows the same code to handle RhsContainer being a
// STL-style container and it being a native C-style array.
typedef typename internal::StlContainerView<RhsContainer> RhsView;
typedef typename RhsView::type RhsStlContainer;
typedef typename RhsStlContainer::value_type Second;
typedef internal::ValueType<RhsStlContainer> Second;
const RhsStlContainer& rhs_stl_container =
RhsView::ConstReference(rhs_container);
@ -4879,7 +4882,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
template <typename Container>
inline internal::UnorderedElementsAreArrayMatcher<
typename Container::value_type>
internal::ValueType<Container>>
IsSupersetOf(const Container& container) {
return IsSupersetOf(container.begin(), container.end());
}
@ -4936,7 +4939,7 @@ inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
template <typename Container>
inline internal::UnorderedElementsAreArrayMatcher<
typename Container::value_type>
internal::ValueType<Container>>
IsSubsetOf(const Container& container) {
return IsSubsetOf(container.begin(), container.end());
}
@ -5184,13 +5187,13 @@ inline internal::AllOfArrayMatcher<T> AllOfArray(const T (&array)[N]) {
}
template <typename Container>
inline internal::AnyOfArrayMatcher<typename Container::value_type> AnyOfArray(
inline internal::AnyOfArrayMatcher<internal::ValueType<Container>> AnyOfArray(
const Container& container) {
return AnyOfArray(container.begin(), container.end());
}
template <typename Container>
inline internal::AllOfArrayMatcher<typename Container::value_type> AllOfArray(
inline internal::AllOfArrayMatcher<internal::ValueType<Container>> AllOfArray(
const Container& container) {
return AllOfArray(container.begin(), container.end());
}

View file

@ -1640,6 +1640,11 @@ TEST(IsSupersetOfTest, WorksWithMoveOnly) {
helper.Call(MakeUniquePtrs({2}));
}
TEST(IsSupersetOfTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, IsSupersetOf(ContainerWithoutValueType{1, 2}));
}
TEST(IsSubsetOfTest, WorksForNativeArray) {
const int subset[] = {1, 4};
const int superset[] = {1, 2, 4};
@ -1768,6 +1773,11 @@ TEST(IsSubsetOfTest, WorksWithMoveOnly) {
helper.Call(MakeUniquePtrs({2}));
}
TEST(IsSubsetOfTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, IsSubsetOf(ContainerWithoutValueType{1, 2, 3, 4}));
}
// Tests using ElementsAre() and ElementsAreArray() with stream-like
// "containers".
@ -1917,6 +1927,11 @@ TEST(UnorderedElementsAreArrayTest, WorksWithMoveOnly) {
helper.Call(MakeUniquePtrs({2, 1}));
}
TEST(UnorderedElementsAreArrayTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, UnorderedElementsAreArray(container));
}
class UnorderedElementsAreTest : public testing::Test {
protected:
typedef std::vector<int> IntVec;
@ -2127,6 +2142,11 @@ TEST_F(UnorderedElementsAreTest, DescribeNegation) {
" - element #2 is equal to 345"));
}
TEST_F(UnorderedElementsAreTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, UnorderedElementsAre(1, 2, 3));
}
// Tests Each().
INSTANTIATE_GTEST_MATCHER_TEST_P(EachTest);
@ -2221,6 +2241,11 @@ TEST(EachTest, WorksWithMoveOnly) {
helper.Call(MakeUniquePtrs({1, 2}));
}
TEST(EachTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, Each(Gt(0)));
}
// For testing Pointwise().
class IsHalfOfMatcher {
public:
@ -2280,6 +2305,11 @@ TEST(PointwiseTest, MakesCopyOfRhs) {
EXPECT_THAT(lhs, m);
}
TEST(PointwiseTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, Pointwise(Eq(), container));
}
TEST(PointwiseTest, WorksForLhsNativeArray) {
const int lhs[] = {1, 2, 3};
vector<int> rhs;
@ -2486,6 +2516,11 @@ TEST(UnorderedPointwiseTest, WorksWithMoveOnly) {
helper.Call(MakeUniquePtrs({2, 1}));
}
TEST(UnorderedPointwiseTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, UnorderedPointwise(Eq(), container));
}
TEST(PointeeTest, WorksOnMoveOnlyType) {
std::unique_ptr<int> p(new int(3));
EXPECT_THAT(p, Pointee(Eq(3)));
@ -2855,6 +2890,11 @@ TEST(ElementsAreTest, MakesCopyOfArguments) {
EXPECT_THAT(array2, Not(polymorphic_matcher));
}
TEST(ElementsAreTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, ElementsAre(1, 2, 3));
}
// Tests for ElementsAreArray(). Since ElementsAreArray() shares most
// of the implementation with ElementsAre(), we don't test it as
// thoroughly here.
@ -2996,6 +3036,11 @@ TEST(ElementsAreArrayTest, SourceLifeSpan) {
EXPECT_THAT(test_vector, Not(matcher_maker));
}
TEST(ElementsAreArrayTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, ElementsAreArray(container));
}
// Tests Contains().
INSTANTIATE_GTEST_MATCHER_TEST_P(ContainsTest);
@ -3130,6 +3175,11 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
EXPECT_THAT(a, Contains(Not(Contains(5))));
}
TEST(ContainsTest, DeducesValueType) {
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(container, Contains(1));
}
} // namespace
} // namespace gmock_matchers_test
} // namespace testing

View file

@ -279,6 +279,13 @@ TYPED_TEST(ContainerEqTest, DuplicateDifference) {
}
#endif // GTEST_HAS_TYPED_TEST
TEST(ContainerEqTest, DeducesValueType)
{
const ContainerWithoutValueType lhs{1, 2, 3};
const ContainerWithoutValueType rhs{1, 2, 3};
EXPECT_THAT(lhs, ContainerEq(rhs));
}
// Tests that multiple missing values are reported.
// Using just vector here, so order is predictable.
TEST(ContainerEqExtraTest, MultipleValuesMissing) {
@ -1512,6 +1519,12 @@ TEST(AllOfArrayTest, Matchers) {
EXPECT_THAT(1, AllOfArray({Ge(0), Ge(1)}));
}
TEST(AllOfArrayTest, DeducesValueType)
{
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(0, Not(AllOfArray(container)));
}
INSTANTIATE_GTEST_MATCHER_TEST_P(AnyOfArrayTest);
TEST(AnyOfArrayTest, BasicForms) {
@ -1601,6 +1614,12 @@ TEST_P(AnyOfArrayTestP, ExplainsMatchResultCorrectly) {
Explain(g2, 2));
}
TEST(AnyOfArrayTest, DeducesValueType)
{
const ContainerWithoutValueType container{1, 2, 3};
EXPECT_THAT(1, AnyOfArray(container));
}
MATCHER(IsNotNull, "") { return arg != nullptr; }
// Verifies that a matcher defined using MATCHER() can work on

View file

@ -186,6 +186,26 @@ std::string Explain(const MatcherType& m, const Value& x) {
return listener.str();
}
// A minimal container that does not expose value_type
class ContainerWithoutValueType {
using UnderlyingContainer = std::vector<int>;
public:
ContainerWithoutValueType(std::initializer_list<int> const args) : _v{args} {}
UnderlyingContainer::const_iterator begin() const { return _v.begin(); }
UnderlyingContainer::const_iterator end() const { return _v.end(); }
UnderlyingContainer::size_type size() const { return _v.size(); }
friend bool operator==(ContainerWithoutValueType const& lhs,
ContainerWithoutValueType const& rhs) {
return lhs._v == rhs._v;
}
private:
UnderlyingContainer _v;
};
} // namespace gmock_matchers_test
} // namespace testing