Generic comparison operator for structs - c++

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?

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.

Related

std::set iterator with same key and different comparators

While trying to solve a problem I started thinking about this - given a user-defined class and 2 comparators for it, lets say we have 2 sets std::set<user_class,comparator_inc> and std::set<user_class,comparator_dec> where the comparators sort by increasing and decreasing value on a value in the user_class(a simple int perhaps). Here's my code:
#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;
struct A
{
int val;
};
struct c_inc
{
bool operator()(const A& first,const A& second) const
{
return first.val > second.val;
}
};
struct c_dec
{
bool operator()(const A& first,const A& second) const
{
return first.val < second.val;
}
};
int main()
{
set<A,c_inc> s1;
set<A,c_dec> s2;
auto x = s1.insert({1});
cout << x.first->val << endl;
x = s2.insert({1});
x = s2.insert({0});
cout << x.first->val << endl;
}
My question is: Is it defined behavior to re-assign x to the output of insert into a set with same Key but different comparator? Is there a problem with this kind of use? Is it defined in the standard somewhere what it should be or is it implementation dependent?
Since the code compiles I think that the return type of insert in both cases is the same - is this assumption correct?
I think it's implementation dependent.
Conceptually the return type of s1.insert and s2.insert are different; especially they have different iterator types, i.e. std::set<A,c_inc>::iterator and std::set<A,c_dec>::iterator. And how the std::set::iterator's type is defined is implementation-defined.
[set.overview]/2
using iterator = implementation-defined; // see [container.requirements]
using const_iterator = implementation-defined; // see [container.requirements]
Technically speaking, you shouldn't rely on this.
Since the code compiles I think that the return type of insert in both
cases is the same - is this assumption correct?
No, it is not. Imagine this simple example:
template<class T>
struct set {
struct iterator { /*...*/ };
};
In this case set<int>::iterator is definitely different from set<double>::iterator.
The implementation is free to implement the iterator type as a free class though (since the iterator does not depend on the comparator), which seems to be the case in the major implementations, and is what's allowing your example.

c++ auto-comparator for structs

There is many primitive structs (several hundreds), that are used to transfer data between two components (for example a player and a server). There are no methods in them, just raw data.
The task is to write all requests and answers to be able to replay a player scenario without a server (we remember all question and all answers, that are pure functions).
So the task is put this structs in map without comparator. Now we use memcmp, it allows not to think about changes in this structs and it is compact, but there are too many problems with padding and etc.
Is it possible to get smth like getHashValue or any default comparator with metaprogramming in c++?
Conditions:
1) I do not want to create a comparator for each struct.
2) I want to have an error if a field was added or deleted if it breaks existing behavior and needs fix.
3) I don't want to change header files with struct definitions.
Example of a struct
struct A {
int a;
int b;
c c;
}
bool operator<(const A& a1, const A& a2)
{
if (a1.a != a2.a) return a1.a < a2.a;
if (a1.b != a2.b) return a1.b < a2.b;
if (a1.c != a2.c) return a1.c < a2.c;
return false;
}
I can consider other languages to implement this exact part (collect questions/answers), if it will not require to describe all this structs on that language again.
In C++17 you can pull this off if you are willing to (A) hard code how many elements are in each struct somewhere, and (B) write or generate code for each count of number of elements in the struct.
template<std::size_t N>
using size_k = std::integral_constant<std::size_t, N>;
template<class T>
auto auto_tie( size_k<0>, T&& t ) {
return std::tie();
}
template<class T>
auto auto_tie( size_k<1>, T&& t ) {
auto& [ x0 ] = std::forward<T>(t);
return std::tie( x0 );
}
template<class T>
auto auto_tie( size_k<2>, T&& t ) {
auto& [ x0, x1 ] = std::forward<T>(t);
return std::tie( x0, x1 );
}
// etc
now, in the namespace of the struct in question, write
struct foo {
int x;
};
struct bar {
int a, b;
};
size_k<1> elems( foo const& ) { return {}; }
size_k<2> elems( bar const& ) { return {}; }
an elems function that return the size_k counting how many elements.
Now in the namespace of the structs, write:
template<class T, class Size=decltype(elems(std::declval<T const&>()))>
bool operator<( T const& lhs, T const& rhs ) {
return auto_tie( Size{}, lhs ) < auto_tie( Size{}, rhs );
}
and you are done.
Test code:
foo f0{1}, f1{2};
bar b0{1,2}, b1{-7, -3};
std::cout << (f0<f1) << (f1<f0) << (f0<f0) << "\n";
std::cout << (b0<b1) << (b1<b0) << (b0<b0) << "\n";
Live example.
Getting further than this will require writing 3rd party tools or waiting for reflection extension to C++, maybe in C++20 or 23.
If you get elems wrong, I believe the structured bindings code in auto_tie should generate an error.
I suppose you could write your own compare operator based upon memcmp.
bool operator<(const A &lhs, const A &rhs) {
return memcmp(&lhs, &rhs, sizeof(A)) < 0;
}
Off course, writing these for each object might be a burden, so you could write a template for this. Though without some SFINAE it will cover too much types.
#include <type_traits>
#include <cstring>
template<typename T>
std::enable_if_t<std::is_pod_v<std::decay_t<T> //< Check if POD
&& !std::is_fundamental_v<std::decay_t<T>>>, //< Don't override for fundamental types like 'int'
bool>
operator<(const T &lhs, const T &rhs) {
return memcmp(&lhs, &rhs, sizeof(std::decay_t<T>)) < 0;
}
EDIT: Note that this technique requires you to zero-initialize the structs.
Looks like the best way to do it is to write a generator, that will generate .h and .cpp with bool operator< for all types in this header file. Then add this project as pre-build step to the main.
It doesn't look like a nice solution, but it allows to avoid hand-written code duplication and will support adding/removing new structs/fields. So I didn't find a better way.

std::unordered_set of pointers

I have the following struct
struct MyClass {
int myInt;
std::map<int, int> myMap;
};
I want to use unordered_set<MyClass*, PointedObjHash, PointedObEq> but I can't find a valid way to declare PointedObEq.
I tried
struct PointedObjHash {
size_t operator() (MyClass* const& c) const {
std::size_t seed = 0;
boost::hash_combine(seed, c->myInt);
boost::hash_combine(seed, c->myMap);
return seed;
}
and I hope it is fine, but I can't find a way to declare PointedObjEq
--- EDIT ---
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
struct MyClass {
...
...
bool operator==(MyClass* const& c) {
return this->myInt == c->myInt & this->myMap == c->myMap;
}
If declare operator== inside the class debug never breaks, but I think 'cause MyClass == MyClass* never happens...
The unordered_set needs to use operator== (or PointedObjEq) to double-check the results of the hash function. The hash provides approximate equality, the equality function is used to weed out false positives.
If you've tested adding the same value to the set twice, then you've tested the equality function. To be sure, of course, you can have it print something to the console.
Since it's impossible to define an operator== function with two pointer operands, the PointedObjEq class will be necessary. Note that it takes a MyClass const * on both sides. Also, there's no need to use a reference to a pointer.
So,
struct PointedObjEq {
bool operator () ( MyClass const * lhs, MyClass const * rhs ) const {
return lhs->myInt == rhs->myInt
&& lhs->myMap == rhs->myMap;
}
};
This should do:
struct PointedObEq {
bool operator()(MyClass const * lhs, MyClass const * rhs) const {
return lhs->myInt == rhs->myInt && lhs->myMap == rhs->myMap;
}
};
The reason why your solution does not work is because you have effectively written a mechanism to compare a MyClass with a MyClass*, when you actually need something to compare a MyClass* with a MyClass*.
P.S.: My original answer passed the pointers by const&. Thinking about it, that's a strange coding style, so I changed it to pass the pointers by value.
typedef MyClass* PtrMyClass;
struct PointedObjCompare
{ // functor for operator==
bool operator()(const PtrMyClass& lhs, const PtrMyClass& rhs) const
{
// your code goes here
}
};
std::unordered_set < MyClass*, PointedObjHash, PointedObjCompare > myset;

How to create a set with my customized comparison in c++

Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.

Is unordered_set<reference_wrapper<Ty>> valid?

Is this valid C++ (considering the latest standard)? I'm getting compilation errors with near-top-of-tree clang/libc++ on Ubuntu 12.04. If it should be valid, I'll mail the clang-dev list with error messages and such.
#include <functional>
#include <unordered_set>
struct X
{
int i;
};
void f ()
{
std::unordered_set<std::reference_wrapper<X>> setOfReferencesToX;
// Do stuff with setOfReferencesToX
}
** As an aside, I'm tired of qualifying that the question/answer is specific to the latest standard. Could the C++ community as a whole, please start qualifying things that are specific to the old standard instead? The newer standard has been out for about a year now.
The problem is not specific to std::reference_wrapper<T>, but rather to the type X itself.
The issue is that std::unordered_set requires that you define hashing and equality functors for std::reference_wrapper<X>. You can pass the hash functor as second template parameter.
For example, this would work:
#include <functional> // for std::hash<int>
struct HashX {
size_t operator()(const X& x) const {
return std::hash<int>()(x.i);
}
};
and then
std::unordered_set<std::reference_wrapper<X>, HashX> setOfReferencesToX;
Another option is to make a specialization for std::hash<X>:
namespace std {
template <>
struct hash<X> {
size_t operator()(const X& x) const {
return std::hash<int>()(x.i);
}
};
}
This allows you to avoid explicitly specifying the 2nd template argument:
std::unordered_set<std::reference_wrapper<X>> setOfReferencesToX;
Concerning the equality comparison, you can fix this by providing an equality operator for class X:
struct X
{
bool operator==(const X& rhs) const { return i == rhs.i; }
int i;
};
Otherwise, you can define your own functor and pass it as third template argument.