Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Test can't find user provided equality operator

Tags:

c++

googletest

I'm using Google Test v1.7

I've created a custom operator == which ASSERT_EQ can't find, but which can be found if used directly. Here's the code

#include <vector>
#include <deque>

#include "gtest/gtest.h"

template< typename T> struct bar { T b; };

template< typename T>
bool operator == ( const std::vector<T>& v, const bar<T>& b ) { return false; }

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d) { return false; }

TEST( A, B ) {

    std::vector<char> vec;
    std::deque<char> deq;
    bar<char> b;

    // compiles
    ASSERT_EQ( vec, b );

    // compiles
    vec == deq;

    // doesn't compile
    ASSERT_EQ( vec, deq );
}

The ASSERT_EQ( vec, deq ) line results in the following message from Apple 6.0 clang:

test/gtest.h:18861:16: error: invalid operands to binary expression ('const std::__1::vector<char, std::__1::allocator<char> >' and 'const
      std::__1::deque<char, std::__1::allocator<char> >')
  if (expected == actual) {
      ~~~~~~~~ ^  ~~~~~~
../x86_64-linux_debian-7/tests/gtest/gtest.h:18897:12: note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<std::__1::vector<char,
      std::__1::allocator<char> >, std::__1::deque<char, std::__1::allocator<char> > >' requested here
    return CmpHelperEQ(expected_expression, actual_expression, expected,
           ^
tst.cc:27:5: note: in instantiation of function template specialization 'testing::internal::EqHelper<false>::Compare<std::__1::vector<char, std::__1::allocator<char> >,
      std::__1::deque<char, std::__1::allocator<char> > >' requested here
        ASSERT_EQ( vec, deq );
        ^

while gcc 4.7.2 lists all of the templates it tried and failed to make expected == actual work, ignoring the one I provided.

What I don't understand is why

  • ASSERT_EQ( vec, b ) finds the provided operator ==; and
  • vec == deq compiles; but
  • ASSERT_EQ( vec, deq ) doesn't.

Could someone please shine some light on this? It's got to be something blazingly obvious, but I can't see it.

like image 817
Diab Jerius Avatar asked Aug 20 '16 02:08

Diab Jerius


2 Answers

Your problem is due to the ADL (argument dependent lookup). std::vector and std::deque, as you already know, are defined in std namespace, but your are defining the operator== at global namespace and ADL is failing to locate this function.

To solve your problem, you must define operator== in the same namespace of your containers, that is inside std namespace. The problem is that your are NOT ALLOWED to do that. In this way, I would suggest you to just change a bit of your approach. Why not try something like that:

template< typename T>
bool equal( const std::vector<T>& v , const std::deque<T>& d) { return false; }

TEST( A, B ) {
    std::vector<char> vec;
    std::deque<char> deq;
    ASSERT_TRUE( equal(vec, deq) );
}
like image 151
Amadeus Avatar answered Oct 01 '22 03:10

Amadeus


The short answer:

The failure to find the correct operator== template is primarily due to the fact that Google Test defines it's own operator== template and the namespace lookup rule which does not use ADL chooses that template and rejects it. ADL, as pointed out by Amadeus, cannot hope to find the template that I defined.

The solution:

As Amadeus points out, moving things into the std namespace (in this case to get ADL to work) is discouraged.

I have no qualms about polluting Google Test's namespace, so moving my template into ::testing::internals solves the problem (using ordinary lookup, not ADL).

The longer answer:

My expectation was that the global operator== should be discovered via what Vandevoorde & Josuttis, C++ Templates Section 9.2, p. 122, term ordinary lookup.

Here is code illustrating this:

#include <vector>
#include <deque>

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d);

namespace A {

    template <typename T1, typename T2>
    bool EQ( const T1& expected, const T2& actual ) {
        return expected == actual;
    }
}

void TestBody() {

    std::vector<char> vec;
    std::deque<char> deq;

    ::A::EQ(vec, deq) ;
}

This successfully compiles. My reading of Amadeus' answer is that Amadeus believes that should fail due to ADL. However, in this case ADL is not used to find operator==. This can be demonstrated by explicitly turning off ADL in the call to the operator by rewriting

expected == actual

as

return (operator==)( expected, actual);

V&J Section 9.2.1, p.123:

ADL is also inhibited if the name of the function to be called is enclosed in parentheses

in which case the code still compiles.

In order to determine why the code involving Google Test failed, I performed rather extreme surgery upon the Google Test headers until I extracted just the code causing the compiler errors, which led to a definition of operator== in the testing::internal namespace in gtest/internal/gtest-linked_ptr.h:

namespace testing {
namespace internal {
[...]
template<typename T> inline
bool operator==(T* ptr, const linked_ptr<T>& x) {
  return ptr == x.get();
}
[...]
}
}

Translating that into my test code results in:

#include <vector>
#include <deque>

template< typename T>
bool operator==(  const std::vector<T>& v , const std::deque<T>& d);

namespace A {

    struct S {};
    template<typename T> bool operator==(T* ptr, S& x);

    template <typename T1, typename T2>
    bool EQ( const T1& expected, const T2& actual ) {
      return expected == actual;
    }
}

void TestBody() {

    std::vector<char> vec;
    std::deque<char> deq;

    ::A::EQ(vec, deq) ;
}

This successfully fails to compile with the unfound template errors. Of interest is the first message of failure:

gtst.cc: In instantiation of ‘bool A::EQ(const T1&, const T2&) [with T1 = std::vector<char>; T2 = std::deque<char>]’:
gtst.cc:25:21:   required from here
gtst.cc:14:37: error: no matching function for call to ‘operator==(const std::vector<char>&, const std::deque<char>&)’
gtst.cc:14:37: note: candidates are:
gtst.cc:10:31: note: template<class T> bool A::operator==(T*, A::S&)
gtst.cc:10:31: note:   template argument deduction/substitution failed:
gtst.cc:14:37: note:   mismatched types ‘T*’ and ‘std::vector<char>’

So, it is first looking at A::operator==.

Stroustrup, The C++ Programming Language, 4th Edition, Section 26.3.5 p. 753 states that the binding of dependent names is done by looking at

  1. The names in scope at the point where the template is defined, plus
  2. the names in the namespace of an argument of a dependent call

In this case, by the first rule, A::operator== should be chosen rather than ::operator==. ADL also fails to find ::operator== because, as Amadeus points out, it is not in the std namespace.

To convince myself that the compile failure is indeed the result of the first rule, I moved the definition of ::operator== into the A namespace and turned off ADL as before.

The code successfully compiles.

like image 43
Diab Jerius Avatar answered Oct 01 '22 03:10

Diab Jerius