digitalmars.D.learn - finding composed structs
- "Dan" <dbdavidson yahoo.com> Oct 30 2012
- "Tobias Pankrath" <tobias pankrath.net> Oct 30 2012
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> Oct 30 2012
- Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> Oct 30 2012
- Timon Gehr <timon.gehr gmx.ch> Oct 30 2012
- "Dan" <dbdavidson yahoo.com> Oct 30 2012
- "Tobias Pankrath" <tobias pankrath.net> Oct 30 2012
- "Tobias Pankrath" <tobias pankrath.net> Oct 30 2012
- "Dan" <dbdavidson yahoo.com> Oct 30 2012
- "Tobias Pankrath" <tobias pankrath.net> Oct 30 2012
- "Tobias Pankrath" <tobias pankrath.net> Oct 30 2012
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
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
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
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
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
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
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
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
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
Beautiful version: http://dpaste.dzfl.pl/2340d73f
Y u no short circuit? Andrei
Left as an exercise to the reader.
Oct 30 2012
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









"Tobias Pankrath" <tobias pankrath.net> 