Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic comparison operator for structs

In many of my unit tests I need to compare the contents of simple structs having only data members:

struct Object {
  int start;
  int stop;
  std::string message;
}

Now, if I want to write something like:

CHECK(object1==object2);

I always have to implement:

bool operator==(const Object& lhs, const Object& rhs) {
   return lhs.start==rhs.start && lhs.stop==rhs.stop && lhs.message=rhs.message;
}

Writing all these comparison functions becomes tedious, but is also prone to errors. Just imagine, what will happen if I add a new data member to Object, but the comparison operator will not be updated.

Then I remembered my knowledge in Haskell and the magic deriving(Eq) directive, which just generates a sane comparison function for free.

How, could I derive something similar in C++?

Happily, I figured out that C++17 comes with a generic operator== and that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple.

So I boldly tried the following:

#include <tuple>
#include <iostream>
#include <tuple>

template<typename T>
bool operator==(const T& lhs, const T& rhs)
{
    auto leftTuple = std::make_tuple(lhs);
    auto rightTuple = std::make_tuple(rhs);

    return leftTuple==rightTuple;
}

struct Object
{
    std::string s;
    int i;
    double d;
};

int main(int arg, char** args)
{
    std::cout << (Object{ "H",1,2. } == Object{ "H",1,2. }) << std::endl;
    std::cout << (Object{ "A",2,3. } ==  Object{ "H",1,2. }) << std::endl;
    return EXIT_SUCCESS;
}

But, unfortunately it just doesn't compile and I really don't know why. Clang tells me:

main.cpp:11:18: error: use of overloaded operator '==' is ambiguous (with operand types
      'std::tuple<Object>' and 'std::tuple<Object>')
        return leftTuple==rightTuple;

Can I possibly fix this compile error to get my desired behavior?

like image 566
Aleph0 Avatar asked Jul 17 '19 08:07

Aleph0


1 Answers

No, since comparing tuples reverts to comparing the elements of the tuple, so leftTuple == rightTuple tries to compare two Objects which is not possible.

that every struct should be easily convertible to an std::tuple by the virtue of std::make_tuple

No, you'll just get a tuple with one element, the struct.

The trick is to use std::tie:

std::tie(lhs.mem1, lhs.mem2) == std::tie(rhs.mem1, rhs.mem2)

but that has the same problem as your original solution. Unfortunately C++17 doesn't have any facility to avoid this problemyou could write a macro :). But in C++20 you will be able to do:

struct Object
{
    std::string s;
    int i;
    double d;
    bool operator==(const Object &) const = default;
};

which will generate the correct comparison operators for Object.

like image 64
Rakete1111 Avatar answered Oct 09 '22 02:10

Rakete1111