12

I'm having trouble using a custom overloaded '==' operator with PCL and Google Test (GTest)

#include <pcl/point_types.h>
namespace pcl { struct PointXYZ; }

bool operator==(pcl::PointXYZ p1, pcl::PointXYZ p2) {return p1.x-p2.x<.1;}
#include <gtest/gtest.h>

TEST(Foo, bar) {
  pcl::PointXYZ a{2,3,4};
  pcl::PointXYZP b{2,3,4};
  EXPECT_EQ(a,b); // Compile error no match for operator==
}

int main(int argc, char **argv){
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

The error I get is:

|| /usr/include/gtest/gtest.h: In instantiation of 'testing::AssertionResult testing::internal::CmpHelperEQ(const char*, const char*, const T1&, const T2&) [with T1 = pcl::PointXYZ; T2 = pcl::PointXYZ]':
/usr/include/gtest/gtest.h|1361 col 23| required from 'static testing::AssertionResult testing::internal::EqHelper<lhs_is_null_literal>::Compare(const char*, const char*, const T1&, const T2&) [with T1 = pcl::PointXYZ; T2 = pcl::PointXYZ; bool lhs_is_null_literal = false]'
src/foo/src/tests.cpp|20 col 3| required from here
/usr/include/gtest/gtest.h|1325 col 16| error: no match for 'operator==' (operand types are 'const pcl::PointXYZ' and 'const pcl::PointXYZ')
||    if (expected == actual) {
||                 ^
/usr/include/gtest/internal/gtest-linked_ptr.h|213 col 6| note: candidate: template<class T> bool testing::internal::operator==(T*, const testing::internal::linked_ptr<T>&)
||  bool operator==(T* ptr, const linked_ptr<T>& x) {
||       ^
/usr/include/gtest/internal/gtest-linked_ptr.h|213 col 6| note:   template argument deduction/substitution failed:
/usr/include/gtest/gtest.h|1325 col 16| note:   mismatched types 'T*' and 'pcl::PointXYZ'

I tried to adhere to the primer: https://github.com/google/googletest/blob/master/googletest/docs/primer.md#binary-comparison

In particular, my operator is defined before including gtest, and I'm sure the types match up. I also tried writing the overload to take const references, but that just compared the addresses instead of the values.

rold2007
  • 1,297
  • 1
  • 12
  • 25
Andrew Wagner
  • 22,677
  • 21
  • 86
  • 100
  • Try to move `operator ==` in the same namespace than `PointXYZ`. – Jarod42 Oct 27 '15 at 14:57
  • 2
    The error says it is trying to compare `const pcl::PointXYZ`s. What happes if you change `operator==` to `bool operator==(const pcl::PointXYZ& p1, const pcl::PointXYZ& p2) {return p1.x-p2.x<.1;}`? – NathanOliver Oct 27 '15 at 15:01

2 Answers2

22

Operators on custom types are found through argument dependent lookup.

Argument dependent lookup (in a nutshell) means that functions may be considered if they are defined in the same namespace as one or more of their arguments.

This code:

namespace pcl { struct PointXYZ; }

bool operator==(pcl::PointXYZ p1, pcl::PointXYZ p2) {return p1.x-p2.x<.1;}

defines PointXYZ in the pcl:: namespace, but defines the operator== function in the global namespace. Hence, not a candidate for ADL.

doing this:

namespace pcl { 
  struct PointXYZ; 
  bool operator==(pcl::PointXYZ p1, pcl::PointXYZ p2) {return p1.x-p2.x<.1;}
}

Fixes that because now the operator== has the name pcl::operator== which is in the same namespace as one of its arguments (actually in this case both of them, but you get the idea). This makes it a candidate during ADL and thus it will be selected when gtest invokes the equality test.

Michał Góral
  • 1,439
  • 2
  • 16
  • 30
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Thanks; that was a massive hole in my understanding of how namespaces work. It's counterintuitive that functions in the global namespace with compatible parameters aren't candidates, but I suppose its more in line with how classes do namespacing. I wonder if intuitive but invalid code like this might start working if/when C++ gets UFCS. – Andrew Wagner Oct 28 '15 at 10:35
  • Before long you'll begin to see that the way it's done is reasonable. It co-locates the operators and the objects to which they relate. In addition it prevents the pollution of the global namespace, which would result in subtle bugs that would be extremely difficult to locate. – Richard Hodges Oct 28 '15 at 10:47
  • @Ankur yes. you'd define operator== to take arguments as const reference. – Richard Hodges Apr 03 '19 at 23:43
  • @RichardHodges I also got the similar problem but found out that EXPECT_TRUE works in this case instead EXPECT_EQ, any idea about that ? – Tejendra Apr 14 '19 at 15:51
  • @Tejendra EXPECT_TRUE requires there to be an implicit or explicit conversion to bool (or probably any numeric type) – Richard Hodges Apr 14 '19 at 16:38
3

Include the operator== definition within the namespace pcl:

#include <pcl/point_types.h>
namespace pcl
{
    bool operator==(pcl::PointXYZ p1, pcl::PointXYZ p2) {return p1.x-p2.x<.1;}
}

Besides, you can remove the forward declaration for pcl::PointXYZ since it must be complete in header pcl/point_types.h. Otherwise, compiler would complain when defining variables within TEST.

If not defined, you will also have to define operator<<(std::ostream&, const pcl::PointXYZ&) so that Google Test can print out your values when the equality assertion fails.

Antonio Pérez
  • 6,702
  • 4
  • 36
  • 61