www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - finding composed structs

reply "Dan" <dbdavidson yahoo.com> writes:
Please help me with any flaws in logic or understanding:

For any struct S, '==' means either bitwise comparison or a call 
to opEquals of S if it exists.
If S has no (dynamic arrays, associative arrays, pointers, or 
class references as members (recursively)) then bitwise compare 
is equivalent to a deep compare.

But if you do have any of those and you want true deep comparison 
semantics, you must implement a correct opEquals at the first 
introduction of those members, and carry that forward through all 
composed structures to S.

struct A { int x = 3; }
struct B { A a; }
struct C { B b; }
struct D { C c; }

So in this case, D has deep equality semantics for ==.
But change A to struct A { string x; } and the deep equality 
semantics for D disappears. To get it back an opEquals must be 
correctly implemented in A, B, C, and D.

Is this accurate?
If true and not an issue, people must not be relying too much on 
deep equality semantics like this. But how else can you find 
things?

Thanks
Dan
Oct 30 2012
next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Tuesday, 30 October 2012 at 15:26:22 UTC, Dan wrote:
 Please help me with any flaws in logic or understanding:

 For any struct S, '==' means either bitwise comparison or a 
 call to opEquals of S if it exists.
 If S has no (dynamic arrays, associative arrays, pointers, or 
 class references as members (recursively)) then bitwise compare 
 is equivalent to a deep compare.

 But if you do have any of those and you want true deep 
 comparison semantics, you must implement a correct opEquals at 
 the first introduction of those members, and carry that forward 
 through all composed structures to S.

 struct A { int x = 3; }
 struct B { A a; }
 struct C { B b; }
 struct D { C c; }

 So in this case, D has deep equality semantics for ==.
 But change A to struct A { string x; } and the deep equality 
 semantics for D disappears. To get it back an opEquals must be 
 correctly implemented in A, B, C, and D.

 Is this accurate?
 If true and not an issue, people must not be relying too much 
 on deep equality semantics like this. But how else can you find 
 things?

 Thanks
 Dan

You are correct. But I argue and I think TDPL says the same, that == should compare all members and is should bitwise compare. However that is currently not the case.
Oct 30 2012
prev sibling next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/30/2012 08:26 AM, Dan wrote:
 Please help me with any flaws in logic or understanding:

 For any struct S, '==' means either bitwise comparison or a call to
 opEquals of S if it exists.
 If S has no (dynamic arrays, associative arrays, pointers, or class
 references as members (recursively)) then bitwise compare is equivalent
 to a deep compare.

 But if you do have any of those and you want true deep comparison
 semantics, you must implement a correct opEquals at the first
 introduction of those members, and carry that forward through all
 composed structures to S.

 struct A { int x = 3; }
 struct B { A a; }
 struct C { B b; }
 struct D { C c; }

 So in this case, D has deep equality semantics for ==.
 But change A to struct A { string x; } and the deep equality semantics
 for D disappears. To get it back an opEquals must be correctly
 implemented in A, B, C, and D.

 Is this accurate?
 If true and not an issue, people must not be relying too much on deep
 equality semantics like this. But how else can you find things?

 Thanks
 Dan

There is the following discussion currently on the main D forum: http://forum.dlang.org/thread/iphhuttpkogmfwpuvfla forum.dlang.org That behavior will be changed: http://forum.dlang.org/thread/iphhuttpkogmfwpuvfla forum.dlang.org?page=4#post-k6a30m:24140h:241:40digitalmars.com Ali
Oct 30 2012
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/30/12 4:17 PM, Tobias Pankrath wrote:
 On Tuesday, 30 October 2012 at 20:16:12 UTC, Tobias Pankrath
 wrote:

 In the meantime you can use this function template to compare structs
 field by field.

 bool oneStepEqual(T,F)(ref T lhs, ref F rhs)
 if(is(Unqual!T == Unqual!F) && is(T == struct))
 {
 bool result = true;
 foreach(mem; __traits(allMembers, T))
 {
 static if(!is(typeof(__traits(getMember, T, mem)) == function))
 {
 result = result && (__traits(getMember, lhs, mem) ==
 __traits(getMember, rhs, mem));
 }
 }
 return result;
 }

Beautiful version: http://dpaste.dzfl.pl/2340d73f

Y u no short circuit? Andrei
Oct 30 2012
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/30/2012 09:16 PM, Tobias Pankrath wrote:
 On Tuesday, 30 October 2012 at 19:16:18 UTC, Dan wrote:
 So until this bug is fixed any time I have any dynamic array,
 including string in struct S, implement opEquals. When the bug is
 fixed I can remove them. What I didn't realize is that including an
 opEquals in A will cause generation of opEquals in B,C,D for free
 (maybe only if called).

 If this is still off base please let me know.

 Thanks
 Dan

In the meantime you can use this function template to compare structs field by field. bool oneStepEqual(T,F)(ref T lhs, ref F rhs) if(is(Unqual!T == Unqual!F) && is(T == struct)) { bool result = true; foreach(mem; __traits(allMembers, T)) { static if(!is(typeof(__traits(getMember, T, mem)) == function)) { result = result && (__traits(getMember, lhs, mem) == __traits(getMember, rhs, mem)); } } return result; }

You might want to shortcut after the first failed comparison.
Oct 30 2012
prev sibling next sibling parent "Dan" <dbdavidson yahoo.com> writes:
On Tuesday, 30 October 2012 at 16:44:02 UTC, Ali Çehreli wrote:
 There is the following discussion currently on the main D forum:
   
 http://forum.dlang.org/thread/iphhuttpkogmfwpuvfla forum.dlang.org

 That behavior will be changed:

 http://forum.dlang.org/thread/iphhuttpkogmfwpuvfla forum.dlang.org?page=4#post-k6a30m:24140h:241:40digitalmars.com

Interesting, thanks! In reading this and (per Tobias' comment) revisiting TDPL I think my original statement was incomplete and the situation is not as bad as I thought.
 For any struct S, '==' means either bitwise comparison or a 
 call to
 opEquals of S if it exists.

From TDPL: Objects of struct type can be compared for equality out of the box with == and ! =. Comparison is carried out member by member and yields false if at least two corresponding members in the compared objects are not equal, and true otherwise. So the issue in my example is not a problem with structs in general, but just a bug related to dynamic arrays only. My fear of having to provide opEquals at each level is unwarranted - the compiler will generate good ones for me (except for dynamic arrays until the bug is fixed). I've convinced myself of this in the following example. http://dpaste.dzfl.pl/14649c64 So until this bug is fixed any time I have any dynamic array, including string in struct S, implement opEquals. When the bug is fixed I can remove them. What I didn't realize is that including an opEquals in A will cause generation of opEquals in B,C,D for free (maybe only if called). If this is still off base please let me know. Thanks Dan
Oct 30 2012
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Tuesday, 30 October 2012 at 19:16:18 UTC, Dan wrote:
 So until this bug is fixed any time I have any dynamic array, 
 including string in struct S, implement opEquals. When the bug 
 is fixed I can remove them. What I didn't realize is that 
 including an opEquals in A will cause generation of opEquals in 
 B,C,D for free (maybe only if called).

 If this is still off base please let me know.

 Thanks
 Dan

In the meantime you can use this function template to compare structs field by field. bool oneStepEqual(T,F)(ref T lhs, ref F rhs) if(is(Unqual!T == Unqual!F) && is(T == struct)) { bool result = true; foreach(mem; __traits(allMembers, T)) { static if(!is(typeof(__traits(getMember, T, mem)) == function)) { result = result && (__traits(getMember, lhs, mem) == __traits(getMember, rhs, mem)); } } return result; }
Oct 30 2012
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Tuesday, 30 October 2012 at 20:16:12 UTC, Tobias Pankrath
wrote:

 In the meantime you can use this function template to compare 
 structs field by field.

 bool oneStepEqual(T,F)(ref T lhs, ref F rhs)
     if(is(Unqual!T == Unqual!F) && is(T == struct))
 {
         bool result = true;
     foreach(mem; __traits(allMembers, T))
     {
         static if(!is(typeof(__traits(getMember, T, mem)) == 
 function))
         {
             result = result && (__traits(getMember, lhs, mem) 
 == __traits(getMember, rhs, mem));
         }
     }
     return result;
 }

Beautiful version: http://dpaste.dzfl.pl/2340d73f
Oct 30 2012
prev sibling next sibling parent "Dan" <dbdavidson yahoo.com> writes:
On Tuesday, 30 October 2012 at 20:17:06 UTC, Tobias Pankrath 
wrote:
 Beautiful version: http://dpaste.dzfl.pl/2340d73f

Beautiful indeed. Does the same approach work for generating correct versions of opCmp, assuming arbitrary order by field comparison as ordered in struct? Also hashing? Thanks Dan
Oct 30 2012
prev sibling next sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
 Beautiful version: http://dpaste.dzfl.pl/2340d73f

Y u no short circuit? Andrei

Left as an exercise to the reader.
Oct 30 2012
prev sibling parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Tuesday, 30 October 2012 at 21:02:22 UTC, Dan wrote:
 On Tuesday, 30 October 2012 at 20:17:06 UTC, Tobias Pankrath 
 wrote:
 Beautiful version: http://dpaste.dzfl.pl/2340d73f

Beautiful indeed. Does the same approach work for generating correct versions of opCmp, assuming arbitrary order by field comparison as ordered in struct? Also hashing? Thanks Dan

Dunno, if __traits(allMembers...) enforces any order on its result. It looks like DMD does use the definition/declaration order, but that's not in any documentation. The opCmp would depend on this. You could sort the fields by name for a defined order.
Oct 30 2012