Re: Beginner problem: casting in opCmp override

2013-07-09 Thread H. S. Teoh
On Mon, Jul 08, 2013 at 05:09:14PM -0700, Jonathan M Davis wrote:
> On Monday, July 08, 2013 16:58:03 H. S. Teoh wrote:
> > On Mon, Jul 08, 2013 at 04:48:05PM -0700, Jonathan M Davis wrote:
> > > On Monday, July 08, 2013 16:38:16 H. S. Teoh wrote:
> > [...]
> > 
> > > > Basically, when you write x==y, the compiler looks for opEquals
> > > > and opCmp. If opEquals is found, then it's rewritten as
> > > > x.opEquals(y); otherwise, if opCmp is found, it's rewritten as
> > > > x.opCmp(y)==0. If neither are found, then the compiler generates
> > > > a default implementation of opEquals, which basically does a
> > > > bitwise comparison of x and y.
> > > 
> > > Actually, what it's supposed to do isn't necessarily a bitwise
> > > comparison.  It's supposed to do a recursive comparison of all of
> > > the members, where it calls == on each of the members. If a
> > > bitwise comparison will do that, then it may end up as a bitwise
> > > comparison for efficiency reasons, but it's not necessarily a
> > > bitwise comparison.  It used to be that it was doing a bitwise
> > > comparison when it wasn't supposed to, but that was fixed fairly
> > > recently (though I don't recall if that fix has been released
> > > yet).
> > > 
> > > Basically, there's no reason to overload opEquals unless you have
> > > a member which you don't want to compare with == (e.g. pointers).
> > 
> > [...]
> > 
> > Unfortunately, this isn't true for opCmp. If you don't define opCmp,
> > the typeinfo of the struct will have a compare function that
> > basically does bitwise comparison. Proof:
> > 
> > struct S {
> > int[] data;
> > }
> > void main() {
> > auto s = S([1,2,3]);
> > auto t = S([1,2,3]);
> > auto u = S([1,2,4]);
> > 
> > assert(s == t);
> > assert(s != u);
> > assert(typeid(s).compare(&s, &t) == 0); // FAILS
> > assert(typeid(s).compare(&s, &u) != 0);
> > }
> > 
> > Should this be filed as a DMD bug?
> 
> Yes. opCmp's behavior should definitely match opEquals' behavior.
[...]

http://d.puremagic.com/issues/show_bug.cgi?id=10588


T

-- 
MSDOS = MicroSoft's Denial Of Service


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Monday, July 08, 2013 16:58:03 H. S. Teoh wrote:
> On Mon, Jul 08, 2013 at 04:48:05PM -0700, Jonathan M Davis wrote:
> > On Monday, July 08, 2013 16:38:16 H. S. Teoh wrote:
> [...]
> 
> > > Basically, when you write x==y, the compiler looks for opEquals and
> > > opCmp. If opEquals is found, then it's rewritten as x.opEquals(y);
> > > otherwise, if opCmp is found, it's rewritten as x.opCmp(y)==0. If
> > > neither are found, then the compiler generates a default
> > > implementation of opEquals, which basically does a bitwise
> > > comparison of x and y.
> > 
> > Actually, what it's supposed to do isn't necessarily a bitwise
> > comparison.  It's supposed to do a recursive comparison of all of the
> > members, where it calls == on each of the members. If a bitwise
> > comparison will do that, then it may end up as a bitwise comparison
> > for efficiency reasons, but it's not necessarily a bitwise comparison.
> > It used to be that it was doing a bitwise comparison when it wasn't
> > supposed to, but that was fixed fairly recently (though I don't recall
> > if that fix has been released yet).
> > 
> > Basically, there's no reason to overload opEquals unless you have a
> > member which you don't want to compare with == (e.g. pointers).
> 
> [...]
> 
> Unfortunately, this isn't true for opCmp. If you don't define opCmp, the
> typeinfo of the struct will have a compare function that basically does
> bitwise comparison. Proof:
> 
>   struct S {
>   int[] data;
>   }
>   void main() {
>   auto s = S([1,2,3]);
>   auto t = S([1,2,3]);
>   auto u = S([1,2,4]);
> 
>   assert(s == t);
>   assert(s != u);
>   assert(typeid(s).compare(&s, &t) == 0); // FAILS
>   assert(typeid(s).compare(&s, &u) != 0);
>   }
> 
> Should this be filed as a DMD bug?

Yes. opCmp's behavior should definitely match opEquals' behavior.

- Jonathan M Davis


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread H. S. Teoh
On Mon, Jul 08, 2013 at 04:48:05PM -0700, Jonathan M Davis wrote:
> On Monday, July 08, 2013 16:38:16 H. S. Teoh wrote:
[...]
> > Basically, when you write x==y, the compiler looks for opEquals and
> > opCmp. If opEquals is found, then it's rewritten as x.opEquals(y);
> > otherwise, if opCmp is found, it's rewritten as x.opCmp(y)==0. If
> > neither are found, then the compiler generates a default
> > implementation of opEquals, which basically does a bitwise
> > comparison of x and y.
> 
> Actually, what it's supposed to do isn't necessarily a bitwise
> comparison.  It's supposed to do a recursive comparison of all of the
> members, where it calls == on each of the members. If a bitwise
> comparison will do that, then it may end up as a bitwise comparison
> for efficiency reasons, but it's not necessarily a bitwise comparison.
> It used to be that it was doing a bitwise comparison when it wasn't
> supposed to, but that was fixed fairly recently (though I don't recall
> if that fix has been released yet).
> 
> Basically, there's no reason to overload opEquals unless you have a
> member which you don't want to compare with == (e.g. pointers).
[...]

Unfortunately, this isn't true for opCmp. If you don't define opCmp, the
typeinfo of the struct will have a compare function that basically does
bitwise comparison. Proof:

struct S {
int[] data;
}
void main() {
auto s = S([1,2,3]);
auto t = S([1,2,3]);
auto u = S([1,2,4]);

assert(s == t);
assert(s != u);
assert(typeid(s).compare(&s, &t) == 0); // FAILS
assert(typeid(s).compare(&s, &u) != 0);
}

Should this be filed as a DMD bug?


T

-- 
A computer doesn't mind if its programs are put to purposes that don't match 
their names. -- D. Knuth


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Monday, July 08, 2013 16:48:05 Jonathan M Davis wrote:
> On Monday, July 08, 2013 16:38:16 H. S. Teoh wrote:
> > On Tue, Jul 09, 2013 at 12:35:32AM +0200, Ugbar Ikenaki wrote:
> > > Here's one more question:
> > > 
> > > Before I knew that opEquals existed, I tried overloading the
> > > equality expressions (== and !=) in opBinary using the code below.
> > > It worked. Why would the overloaded opBinary version get called if
> > > the equality expressions are held in opEquals?
> > 
> > [...]
> > 
> > I doubt opBinary was actually called. Probably what happened was that ==
> > defaulted to the built-in implementation of opEquals, which simply
> > performs a bitwise comparison of the struct/class, and it just so
> > happened that it was close to (or the same) as what you intended.
> > 
> > Basically, when you write x==y, the compiler looks for opEquals and
> > opCmp. If opEquals is found, then it's rewritten as x.opEquals(y);
> > otherwise, if opCmp is found, it's rewritten as x.opCmp(y)==0. If
> > neither are found, then the compiler generates a default implementation
> > of opEquals, which basically does a bitwise comparison of x and y.
> 
> Actually, what it's supposed to do isn't necessarily a bitwise comparison.
> It's supposed to do a recursive comparison of all of the members, where it
> calls == on each of the members. If a bitwise comparison will do that, then
> it may end up as a bitwise comparison for efficiency reasons, but it's not
> necessarily a bitwise comparison. It used to be that it was doing a bitwise
> comparison when it wasn't supposed to, but that was fixed fairly recently
> (though I don't recall if that fix has been released yet).
> 
> Basically, there's no reason to overload opEquals unless you have a member
> which you don't want to compare with == (e.g. pointers).

I should point out though (in case someone misunderstands) is that that 
applies specifically to structs. You always have to overload opEquals on 
classes if you want == to work properly (since Object's opEquals does a 
comparison of the references rather than the objects).

- Jonathan M Davis


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Monday, July 08, 2013 16:38:16 H. S. Teoh wrote:
> On Tue, Jul 09, 2013 at 12:35:32AM +0200, Ugbar Ikenaki wrote:
> > Here's one more question:
> > 
> > Before I knew that opEquals existed, I tried overloading the
> > equality expressions (== and !=) in opBinary using the code below.
> > It worked. Why would the overloaded opBinary version get called if
> > the equality expressions are held in opEquals?
> 
> [...]
> 
> I doubt opBinary was actually called. Probably what happened was that ==
> defaulted to the built-in implementation of opEquals, which simply
> performs a bitwise comparison of the struct/class, and it just so
> happened that it was close to (or the same) as what you intended.
> 
> Basically, when you write x==y, the compiler looks for opEquals and
> opCmp. If opEquals is found, then it's rewritten as x.opEquals(y);
> otherwise, if opCmp is found, it's rewritten as x.opCmp(y)==0. If
> neither are found, then the compiler generates a default implementation
> of opEquals, which basically does a bitwise comparison of x and y.

Actually, what it's supposed to do isn't necessarily a bitwise comparison. 
It's supposed to do a recursive comparison of all of the members, where it 
calls == on each of the members. If a bitwise comparison will do that, then it 
may end up as a bitwise comparison for efficiency reasons, but it's not 
necessarily a bitwise comparison. It used to be that it was doing a bitwise 
comparison when it wasn't supposed to, but that was fixed fairly recently 
(though I don't recall if that fix has been released yet).

Basically, there's no reason to overload opEquals unless you have a member 
which you don't want to compare with == (e.g. pointers).

- Jonathan M Davis


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread H. S. Teoh
On Tue, Jul 09, 2013 at 12:35:32AM +0200, Ugbar Ikenaki wrote:
> Here's one more question:
> 
> Before I knew that opEquals existed, I tried overloading the
> equality expressions (== and !=) in opBinary using the code below.
> It worked. Why would the overloaded opBinary version get called if
> the equality expressions are held in opEquals?
[...]

I doubt opBinary was actually called. Probably what happened was that ==
defaulted to the built-in implementation of opEquals, which simply
performs a bitwise comparison of the struct/class, and it just so
happened that it was close to (or the same) as what you intended.

Basically, when you write x==y, the compiler looks for opEquals and
opCmp. If opEquals is found, then it's rewritten as x.opEquals(y);
otherwise, if opCmp is found, it's rewritten as x.opCmp(y)==0. If
neither are found, then the compiler generates a default implementation
of opEquals, which basically does a bitwise comparison of x and y.


T

-- 
Arise, you prisoners of Windows
Arise, you slaves of Redmond, Wash,
The day and hour soon are coming
When all the IT folks say "Gosh!"
It isn't from a clever lawsuit
That Windowsland will finally fall,
But thousands writing open source code
Like mice who nibble through a wall.
-- The Linux-nationale by Greg Baker


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Tuesday, July 09, 2013 00:35:32 Ugbar Ikenaki wrote:
> Here's one more question:
> 
> Before I knew that opEquals existed, I tried overloading the
> equality expressions (== and !=) in opBinary using the code
> below. It worked. Why would the overloaded opBinary version get
> called if the equality expressions are held in opEquals? I'm just
> interested in knowing how dmd chooses where to take an expression
> from if it lies in multiple places (e.g. == in opEquals and
> opBinary), even if what I did was just add the == operator to
> opBinary. (Is that what I did there?)
> 
> override bool opBinary( string op ) ( Rect r ) if( op ==
> "==" ) {
>   //Check if rectangle coordinates are equal
>   if( left == r.left && right == r.right && top == r.top &&
> bottom == r.bottom ) {
>   return true;
>   } else {
>   return false;
>   }
>   }
> 
> 
> Thanks!

I don't believe that that will ever work. If you want to overload == and !=, 
use opEquals. opBinary isn't used for that. Look at 
http://dlang.org/operatoroverloading.html if you want to see which function to 
use to overload for each operator.

- Jonathan M Davis


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki

Here's one more question:

Before I knew that opEquals existed, I tried overloading the 
equality expressions (== and !=) in opBinary using the code 
below. It worked. Why would the overloaded opBinary version get 
called if the equality expressions are held in opEquals? I'm just 
interested in knowing how dmd chooses where to take an expression 
from if it lies in multiple places (e.g. == in opEquals and 
opBinary), even if what I did was just add the == operator to 
opBinary. (Is that what I did there?)


   override bool opBinary( string op ) ( Rect r ) if( op == 
"==" ) {

//Check if rectangle coordinates are equal
		if( left == r.left && right == r.right && top == r.top && 
bottom == r.bottom ) {

return true;
} else {
return false;
}
}


Thanks!


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki

And thanks for that pointer, too, H.S.


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki
Wow! You guys are fast and helpful! Thank you Jonathan and Ali! I 
already got it to work, and Ali, your book is great! I've read 
about the first 150 pages. I'll check out those operator 
overloading chapters for sure.


-U


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread H. S. Teoh
On Mon, Jul 08, 2013 at 02:42:30PM -0700, Jonathan M Davis wrote:
> On Monday, July 08, 2013 23:31:14 Ugbar Ikenaki wrote:
> > Also…Rat is a struct, not a class. Herein might lie the problem.
> 
> So, this is Rat's opCmp, correct? If it's a struct, it makes no sense
> for it to take an Object. Object is the base class for all classes and
> has nothing to do with structs. opCmp needs to take Rat, not Object.
> And to be fully functional, you might need to have a ref overload as
> well (since sometimes the compiler and runtime get overly picky about
> exact signatures - which is slowly getting fixed fortunately). So,
> something like this should work:
> 
> int opCmp(Rat other)
> {
> return this.opCmp(other);
> }
> 
> int opCmp(ref Rat other)
[...]

The second overload above should be:

int opCmp(ref const Rat other) const

otherwise the typeinfo of Rat will be wrong, and using Rat in AA's will
not work correctly. (DMD is very picky about the exact signature of
opCmp when it's generating typeinfos -- as I found out yesterday.)


T

-- 
I think the conspiracy theorists are out to get us...


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ali Çehreli

On 07/08/2013 02:21 PM, Ugbar Ikenaki wrote:

I'm trying to implement rational numbers with fully functioning
comparison operators, but I can't seem to figure out why I'm getting
this error and any help would be appreciated:

Error: cannot cast from object.Object to Rat

The code I have is as follows:

int opCmp( Object o ) {
 Rat other = cast(Rat) o; <--- this line throws the error
 …
 //stuff to return -1, 0, or 1 to opCmp with rational numbers
for <, >=, etc.
 …
}

Thanks to anyone who can help!


Yeah, the signature of opCmp is different for structs so you don't need 
to cast.


Once you have the subtraction defined, opCmp can be as simple as the 
following:


/* Sort order operator: Returns a negative value if this
 * fraction is before, a positive value if this fraction
 * is after, and zero if both fractions have the same sort
 * order. */
int opCmp(const ref Fraction rhs) const
{
immutable result = this - rhs;
/* Being a long, num cannot be converted to int
 * automatically; it must be converted explicitly by
 * 'to' (or cast). */
return to!int(result.num);
}

I have a couple of chapters that go over operator overloading.

Operator Overloading:

  http://ddili.org/ders/d.en/operator_overloading.html

Object is specifically for classes:

  http://ddili.org/ders/d.en/object.html

Ali



Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Monday, July 08, 2013 23:31:14 Ugbar Ikenaki wrote:
> Also…Rat is a struct, not a class. Herein might lie the problem.

So, this is Rat's opCmp, correct? If it's a struct, it makes no sense for it 
to take an Object. Object is the base class for all classes and has nothing to 
do with structs. opCmp needs to take Rat, not Object. And to be fully 
functional, you might need to have a ref overload as well (since sometimes the 
compiler and runtime get overly picky about exact signatures - which is slowly 
getting fixed fortunately). So, something like this should work:

int opCmp(Rat other)
{
return this.opCmp(other);
}

int opCmp(ref Rat other)
{
//do stuff for comparison
}

- Jonathan M Davis


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki

Thanks for the quick response!

Here is the initialization code for my Rat struct. I created a 
GCD function that I've tested to work fine:


import std.stdio, std.exception;

struct Rat {
private long n; //Numerator
private long d; //Denominator



public this( long numerator, long denominator ) {
		enforce( denominator != 0, "Error. Denominator can not be 0. 
(Rat.this)");


//Default 0-value Rat object
if( numerator == 0 || numerator == -0 ) {
n = 0;
d = 1;

} else {
//Assign appropriates signs (+ / -) to numerator
// -x/-y
if( numerator < 0 && denominator < 0 ) {
numerator = -numerator;
denominator = -denominator;
// x/-y
} else if( numerator > 0 && denominator < 0 ) {
numerator = -numerator;
denominator = -denominator;
}

//Find GCD of numerator and denominator
long gcd = gcd( numerator, denominator );

			//Return reduced fraction of numerator and denominator 
(invariant)

n = numerator / gcd;
d = denominator / gcd;
}
}



The following is the code to override the opCmp function:


int opCmp( Object o ) {
Rat other = cast(Rat) o;

long num1 = n;
long den1 = d;
long num2 = other.n;
long den2 = other.d;

//If denominators are not equal to each other...
if( den1 != den2 ) {
			//Set denominators equal to each other (with corresponding 
numerators)

num1 *= den2;
den1 *= den2;

if(den2 < den1) {
num2 *= (den1 / den2);
den2 *= (den1 / den2);
} else {
num2 *= (den2 / den1);
den2 *= (den2 / den1);
}

//Return opCmp int value (-1, 0, 1)
if( num1 - num2 < 0 ) {
return -1;
} else if( num1 - num2 == 0 ) {
return 0;
} else {  //if( num1 - num2 > 0 )
return 1;
}

//If denominators are equal to each other...
} else {
//Less than
if( num1 - num2 < 0 ) {
return -1;
//Equal to
} else if( num1 - num2 == 0 ) {
return 0;
//Greater than
} else {  //if( num1 - num2 > 0 )
return 1;
}
}
}


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki

Also…Rat is a struct, not a class. Herein might lie the problem.


Re: Beginner problem: casting in opCmp override

2013-07-08 Thread Jonathan M Davis
On Monday, July 08, 2013 23:21:59 Ugbar Ikenaki wrote:
> I'm trying to implement rational numbers with fully functioning
> comparison operators, but I can't seem to figure out why I'm
> getting this error and any help would be appreciated:
> 
> Error: cannot cast from object.Object to Rat
> 
> The code I have is as follows:
> 
> int opCmp( Object o ) {
>   Rat other = cast(Rat) o; <--- this line throws the error
>  …
>  //stuff to return -1, 0, or 1 to opCmp with rational
> numbers for <, >=, etc.
>  …
> }
> 
> Thanks to anyone who can help!

You're going to need to provide more code than that. Without knowing anything 
about Rat, we can't help you.

On a side note, I point out that talking about compiler errors as being 
"thrown" is going to cause confusion, as throwing is something completely 
different.

- Jonathan M Davis


Beginner problem: casting in opCmp override

2013-07-08 Thread Ugbar Ikenaki
I'm trying to implement rational numbers with fully functioning 
comparison operators, but I can't seem to figure out why I'm 
getting this error and any help would be appreciated:


Error: cannot cast from object.Object to Rat

The code I have is as follows:

int opCmp( Object o ) {
Rat other = cast(Rat) o; <--- this line throws the error
…
//stuff to return -1, 0, or 1 to opCmp with rational 
numbers for <, >=, etc.

…
}

Thanks to anyone who can help!