From: Anders Dalvander on
On Sep 5, 1:19 am, "Martin T." <0xCDCDC...(a)gmx.at> wrote:
> Is there any way in C++ to automatically generate a memberwise
> operator== / operator!= or do you always have to code these operators
> yourself?

Not automatic, but tuples in boost (and C++1x) makes it easier:

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/ref.hpp>
#include <string>

class foo
{
public:
friend bool operator<(const foo& lhs, const foo& rhs)
{
return
boost::make_tuple(boost::cref(lhs.i), boost::cref(lhs.j)) <
boost::make_tuple(boost::cref(rhs.i), boost::cref(rhs.j));
}

private:
std::string i;
int j;
};

int main()
{
foo f1;
foo f2;
return f1 < f2;
}

Regards,
Anders Dalvander


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Martin T. on
Balog Pal wrote:
> "tohava" <tohava(a)gmail.com>
>>> I just accept that creating equality operators is part
>>> of the coding time and move on.
>> I think that part of the problem with this is that whenever a new
>> member is added to the class, one must remember to update operator ==.
>> Forgetting this can lead to bad behavior. Hence, I do not think this
>> is just about saving coding time.
>
> Now, what is the percentage of classes/structs in a project that has a
> meaningful op==?
> And then, where it is based on full memberwise comparition?
>
> Guess if you find the special case, it will not change members all that
> often...
>
>

The use case for these structs is representing configuration data. As
such there are quite a few of them and I *do* expect them to change
quite frequently. For these items memberwise equality is exactly
appropriate most of the time.
Someone will forget to add to the operator== *especially* if it's only
used in the unit tests.

The workaround I currently use to safeguard against this is:
1.) operator== is defined in the header
2.) Manually added static assert on the size of the struct

inline bool operator==(Cfgdata const& lhs, Cfgdata const& rhs)
{
#ifdef NDEBUG
BOOST_STATIC_ASSERT(sizeof(Cfgdata) == 56); // Check size, so we don't
forget to add new members!
#endif
// ...

cheers,
Martin

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Ulrich Eckhardt on
Balog Pal wrote:
> "tohava" <tohava(a)gmail.com>
>> I think that part of the problem with this is that whenever a new
>> member is added to the class, one must remember to update operator ==.
>> Forgetting this can lead to bad behavior. Hence, I do not think this
>> is just about saving coding time.
>
> Now, what is the percentage of classes/structs in a project that has a
> meaningful op==?
> And then, where it is based on full memberwise comparition?

There is another problem where C++ doesn't provide a satisfactory solution
and that is copying. Actually, that can become a real bummer:

struct X {
X(X const& rhs):
a(rhs.a)
{}
int a;
int b;
};

Imagine such a structure being returned from a function. If RVO is in
effect, the caller will receive the correct data. If not, the returned
object will have its 'b' field uninitialised, but often the memory will
earlier have been populated inside the function with a temporary X, so that
the value will be just right. Change a few lines or call it in a different
context and the fragile thing will fall apart.

Been there, done that, wasted lots of time.

Uli


[x] Introspection for C++ now!

--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Balog Pal on
"Martin T." <0xCDCDCDCD(a)gmx.at>
>> Now, what is the percentage of classes/structs in a project that has a
>> meaningful op==?
>> And then, where it is based on full memberwise comparition?
>>
>> Guess if you find the special case, it will not change members all that
>> often...
>
> The use case for these structs is representing configuration data. As
> such there are quite a few of them and I *do* expect them to change
> quite frequently.

Sure, I use configs, and indeed members come and go. I still can't recall a
case to want op == on the whole config. I read it at some place, possibly
write it out as a bulk, and use individual members... but how comparision
comes in?

> For these items memberwise equality is exactly
> appropriate most of the time.

It could 'work' indeed but for what situation?
(btw I'd guess Boost has tools for such beasts -- tuple, any, ... if plain
old struct is not enough)

> Someone will forget to add to the operator== *especially* if it's only
> used in the unit tests.

Well, if it only used in UT, it is a good indication of unwanted ballast
that has nothing to do in the real interface. The test should not be that
intrusive. And if, for implementation of the harness it needs compare that
way -- I really would go with the earlier suggest: code generator.

> The workaround I currently use to safeguard against this is:
> 1.) operator== is defined in the header
> 2.) Manually added static assert on the size of the struct
>
> inline bool operator==(Cfgdata const& lhs, Cfgdata const& rhs)
> {
> #ifdef NDEBUG
> BOOST_STATIC_ASSERT(sizeof(Cfgdata) == 56); // Check size, so we don't
> forget to add new members!
> #endif
> // ...

Interesting idea really. My first line of thought would be 'scrap it'.

OTOH I do have situations with similar maintain cases -- the 'offending'
functions are mostly I/O routines (like stream op << >> ), and when adding a

member I follow the ritual to pick another member and look visit all
instances...



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Balog Pal on
"Ulrich Eckhardt" <eckhardt(a)satorlaser.com>
>> Now, what is the percentage of classes/structs in a project that has a
>> meaningful op==?
>> And then, where it is based on full memberwise comparition?
>
> There is another problem where C++ doesn't provide a satisfactory solution
> and that is copying. Actually, that can become a real bummer:

Copy and op= is created to do memberwise stuff since like CFront 2.0? Yeah,

getting rid of unwanted ones is not so nice yet (look C++0x for improvements

on all fronts...)

> struct X {
> X(X const& rhs):
> a(rhs.a)
> {}
> int a;
> int b;
> };
>
> Imagine such a structure being returned from a function.

This "problem" is a top gotcha for ~ 2 decades. And has an extremely easy
solution: do NOT write copyctor, op=. I mean it. Not in user code!

Too bad, around the same CFront 2.0 the suggestion went the other way to
mitigate some broken *bitwise* copying compilers. With just homegrown stuff
in early '90-s it may be even justified, but since we have standard, a deal
good libs, and RAII being the norm for ages -- what is really the motivation

to write cctor by hand?

When you do it, omission is only one of the problems, you drag a full basket

of other issues. Slowly "fixing" until the motivator gets out in a base
class or member -- then just to discover you could START with using a smart
pointer or vector or string instead of a fraking raw pointer... ;-o

My experience is to just have two basic cases in user code: forbid cctor and

op= or leave the auto-created. Exceptions are in the 'writing library'
land, where it is increasingly hard to find a case of a new resource or
collection not already covered.

> If RVO is in
> effect, the caller will receive the correct data.

It's plain case of UB. The other usual screw variant is to list all the
members -- but forget the base class. That will pick up default init --
likely "work" just do not what ecpected.

> Been there, done that, wasted lots of time.

And your conclusions on mitigation?



--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]