I wrote a class to help me compare two std::vectors
that won't necessarily have the same length. The class itself is working fine, but GoogleTest isn't using the ==
operator, so using EXPECT_EQ(foo, bar)
fails while EXPECT_TRUE(foo == bar)
works:
#include <vector>
#include <gtest/gtest.h>
template<class T>
class MyVector: public std::vector<T> {
public:
MyVector(std::initializer_list<T> il)
: std::vector<T>(il){}
bool operator== (const std::vector<T>& other)
{
// Need to static_cast so we don't call this function recursively
if( this->size() < other.size() ) {
return (
static_cast<std::vector<T>&>(*this) == std::vector<T>(other.begin(), other.begin() this->size())
);
} else if( this->size() > other.size() ) {
return (
std::vector<T>(this->begin(), this->begin() other.size()) == other
);
}
return (static_cast<std::vector<T>&>(*this) == other);
}
};
TEST(SOME_TEST, TestMyVector) {
MyVector<int> foo {1, 2, 3, 4};
std::vector<int> bar {1, 2, 3, 4, 5};
EXPECT_TRUE(foo == bar);
EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3, 4}));
EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3}));
EXPECT_TRUE(foo == (std::vector<int>{1, 2, 3, 4, 5}));
EXPECT_EQ(foo, bar);
}
When I run the above test, I get:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from SOME_TEST
[ RUN ] SOME_TEST.TestMyVector
./my_test.cpp:38: Failure
Expected equality of these values:
foo
Which is: { 1, 2, 3, 4 }
bar
Which is: { 1, 2, 3, 4, 5 }
[ FAILED ] SOME_TEST.TestMyVector (0 ms)
[----------] 1 test from SOME_TEST (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] SOME_TEST.TestMyVector
1 FAILED TEST
How can I tell GTest to use my class, or what function do I need to write so that EXPECT_EQ
works?
Thanks!
CodePudding user response:
Drew Dormann in comments is right. Your operator ==
requires left-hand side operand to be non-const
, so when GTest passes arguments as const T&
, compiler cannot select your operator
and falls back to operator==(std::vector, std::vector)
.
Making your operator const
-correct makes the test pass:
bool operator== (const std::vector<T>& other) const
{
// Need to static_cast so we don't call this function recursively
if( this->size() < other.size() ) {
return (
static_cast<const std::vector<T>&>(*this) == std::vector<T>(other.begin(), other.begin() this->size())
);
} else if( this->size() > other.size() ) {
return (
std::vector<T>(this->begin(), this->begin() other.size()) == other
);
}
return (static_cast<const std::vector<T>&>(*this) == other);
}
However, this is not the only problem you will encounter, starting from the fact that bar == foo
is different than foo == bar
and that inheriting from standard containers is bad. Consider replacing this with a free function (see it online):
#include <algorithm>
template<class T>
bool begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
// copy-free
auto [i1, i2] = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
bool they_are_equal = i1 == lhs.end() && i2 == rhs.end();
bool one_reached_end = i1 != lhs.end() || i2 != rhs.end();
return they_are_equal || one_reached_end;
//could be also done with C 20 views
}
CodePudding user response:
Thanks for the comments, everyone.
@yksisarvinen, thank you so much for suggesting a non-copy comparison. Unfortunately the begin_eq
function was failing for EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0}));
because in this case i2 != rhs.end()
.
You and Nathan Pierson are correct that a free function is much better, I was over-complicating things by overloading the ==
operator.
My current code is:
#include <algorithm>
#include <gtest/gtest.h>
template<class T>
bool begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
// copy-free
auto [i1, i2] = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
bool they_are_equal = i1 == lhs.end() && i2 == rhs.end();
bool one_reached_end = i1 != lhs.end() || i2 != rhs.end();
return they_are_equal || one_reached_end;
//could be also done with C 20 views
}
template<typename T>
bool another_begin_eq(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
// copy-free
auto lhs_it = lhs.begin();
auto rhs_it = rhs.begin();
size_t size = (lhs.size() <= rhs.size() ? lhs.size() : rhs.size());
for(size_t i = 0; i < size; i ) {
if( *lhs_it != *rhs_it ) return false;
}
return true;
}
TEST(SOME_TEST, TestMyVector) {
std::vector<int> foo {1, 2, 3, 4};
std::vector<int> bar {1, 2, 3, 4, 5};
EXPECT_FALSE(foo == bar);
EXPECT_TRUE(begin_eq(foo, bar));
EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3, 4}));
EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3}));
EXPECT_TRUE(begin_eq(foo, std::vector<int>{1, 2, 3, 4, 5}));
EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 3, 0}));
EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 0}));
EXPECT_FALSE(another_begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5}));
EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0}));
EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 0}));
EXPECT_FALSE(begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5}));
EXPECT_NE(foo, bar);
}
Which yields:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from SOME_TEST
[ RUN ] SOME_TEST.TestMyVector
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:46: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 3, 0})
Actual: true
Expected: false
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:47: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 0})
Actual: true
Expected: false
/untether/workspace/imAIgine/runtimeExports/libuntetherprocessing/tests/test_my_vector.cpp:48: Failure
Value of: begin_eq(foo, std::vector<int>{1, 2, 3, 0, 5})
Actual: true
Expected: false
[ FAILED ] SOME_TEST.TestMyVector (0 ms)
[----------] 1 test from SOME_TEST (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] SOME_TEST.TestMyVector