How to compare objects of POD types

Viewed 2813

This example :

#include <iostream>
#include <cstring>

struct A
{
    int  a;
    bool b;
};

bool foo( const A a1, const A a2 )
{
    return ( 0 == std::memcmp( &a1, &a2, sizeof( A ) ) );
}

int main()
{
    A a1 = A();
    a1.a = 5;a1.b = true;
    A a2 = A();
    a2.a = 5;a2.b = true;

    std::cout<<std::boolalpha << foo( a1, a2 ) << std::endl;
}

is going to produce false, because of padding.

I do not have access to the foo function, and I can not change the way the comparison is done.

Assuming a bool occupies 1 byte (that is true on my system), if I change the struct A to this :

struct A
{
  int a;
  bool b;
  char dummy[3];
};

then it works fine on my system (the output is true).

Is there anything else I could do to fix the above problem (get the true output)?

3 Answers

Since C++11 we can use tuples for simple POD comparison (tuples use lexicographical comparison for >, <, >= and <= operators, more info on that: https://en.cppreference.com/w/cpp/utility/tuple/operator_cmp ) :

#include <iostream>
#include <tuple>

struct Point {
    int x;
    int y;
    int z;    
};


auto pointToTuple(const Point& p) {
    return std::make_tuple(p.x, p.y, p.z);
}

bool operator==(const Point& lhs, const Point& rhs ) {
    return pointToTuple(lhs) == pointToTuple(rhs);
}

bool operator<(const Point& lhs, const Point& rhs ) {
    return pointToTuple(lhs) < pointToTuple(rhs);
}

int main()
{

    Point a{1, 2, 3};
    Point b{1, 2, 3};
    Point c{2, 2, 2};

    std::cout << (pointToTuple(a) == pointToTuple(b) ? "true" : "false") << "\n"; //true
    std::cout << (pointToTuple(a) == pointToTuple(c) ? "true" : "false") << "\n"; //false

    std::cout << (a == b ? "true" : "false") << "\n"; //true
    std::cout << (a == c ? "true" : "false") << "\n"; //false

    std::cout << (a < b ? "true" : "false") << "\n"; //false
    std::cout << (a < c ? "true" : "false") << "\n"; //true

}

C++20 should bring us default comparisons (https://en.cppreference.com/w/cpp/language/default_comparisons). So if class defines operator<=> as defaulted, compiler will automatically generate ==, !=, <, <=, > and >= operators and code for them:

struct Point {
    int x;
    int y;
    int z;    

    auto operator<=>(const Point&) const = default;
};

In C++14 and above you can use this library: https://github.com/apolukhin/magic_get/ to extract member types of POD. Than you can write generic comparison operator, which does not require memsetting memory of original object to erase padding like this:

#include "boost/pfr/precise.hpp"
template<typename T>
void foo(const T& a, const T& b)
{
    return boost::pfr::flat_less<T>{}(a, b);
}

This method has the advantage of not modifying code that creates objects (which can be valuable when it's not under your control), but it also generates additional binary code, and compilation with PFR library will be slower.

Still - it's most flexible and clean, since simple memcmp does not give you real semantic power (for example when you use custom comparison operators on subtypes of your PODs).


PS: using PFR library you can do multiple other things with PODs, such as printing them, iterating over members etc. Check more examples here:

http://apolukhin.github.io/magic_get/boost_precise_and_flat_reflectio/short_examples_for_the_impatient.html

Related