www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opEquals(const ref yadaYada)

reply dsimcha <dsimcha yahoo.com> writes:
I've noticed that, for DMD 2.037, we've started mandating that the input
parameter for struct opEquals be const ref T.  This seemed like a good idea
initially, but it creates the horribly leaky abstraction that the right-hand
argument to opEquals can't be an rvalue.  Example:

struct Foo {
    bool opEquals(const ref Foo rhs) const {  // Only signature
                                              // that compiles.
        return true;
    }
}

Foo getFoo() {
    return Foo();
}

void main() {
    Foo foo = getFoo();
    bool isEqual = foo == getFoo();
}

Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
const bool(Foo rhs)

Will this be getting fixed witht he new operator overloading?
Dec 12 2009
next sibling parent reply Richard Webb <webby beardmouse.org.uk> writes:
Hi,
I mentioned this problem in the thread   
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=18405

I ran into the rvalue problem while trying to get Juno to compile, and found
that it builds ok if you declare both

bool opEquals(const ref Foo rhs) const

and

bool opEquals(Foo rhs) const
Dec 12 2009
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
Richard Webb Wrote:

 Hi,
 I mentioned this problem in the thread   
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=18405
 
 I ran into the rvalue problem while trying to get Juno to compile, and found
that it builds ok if you declare both
 
 bool opEquals(const ref Foo rhs) const
 
 and
 
 bool opEquals(Foo rhs) const
 
Note the resulting bug: http://d.puremagic.com/issues/show_bug.cgi?id=3607 I also don't believe Richard's workaround is a "solution". The compiler should not force you to create a const ref version if the argument can be implicitly cast from const to mutable. -Steve
Dec 13 2009
parent lws <invalid email.com> writes:
On 2009-12-13 04:29:28 -0800, Steven Schveighoffer <schveiguy yahoo.com> said:

 Richard Webb Wrote:
 
 Hi,
 I mentioned this problem in the thread  
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=18405
I
 
 ran into the rvalue problem while trying to get Juno to compile, and 
 found that it builds ok if you declare both
 
 bool opEquals(const ref Foo rhs) const
 
 and
 
 bool opEquals(Foo rhs) const
 
Note the resulting bug: http://d.puremagic.com/issues/show_bug.cgi?id=3607 I also don't believe Richard's workaround is a "solution". The compiler should not force you to create a const ref version if the argument can be implicitly cast from const to mutable. -Steve
I don't know if I buy that the opEquals thing is a bug. However, that const destructor thing in the other thread needs to be fixed. //////////////////////////////// struct Foo {         ~this()         {                          } } void Bar() {         const Foo f; } //////////////////////////////// Definitely produces incorrect behavior.
Dec 14 2009
prev sibling parent reply lws <invalid email.com> writes:
I don't know if I believe this is necesarrily bad.  It's revealing some 
bad coding on your part.

You shouldn't be doing opEquals with an rvalue of a class.   Make 
getFoo return a reference.

ref Foo getFoo() {} fixes the problem and avoids value-copying for no 
reason to an rvalue that's going to get garbage collected.

-lws


On 2009-12-12 07:14:50 -0800, dsimcha <dsimcha yahoo.com> said:

 I've noticed that, for DMD 2.037, we've started mandating that the input
 parameter for struct opEquals be const ref T.  This seemed like a good idea
 initially, but it creates the horribly leaky abstraction that the right-hand
 argument to opEquals can't be an rvalue.  Example:
 
 struct Foo {
     bool opEquals(const ref Foo rhs) const {  // Only signature
                                               // that compiles.
         return true;
     }
 }
 
 Foo getFoo() {
     return Foo();
 }
 
 void main() {
     Foo foo = getFoo();
     bool isEqual = foo == getFoo();
 }
 
 Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
 const bool(Foo rhs)
 
 Will this be getting fixed witht he new operator overloading?
Dec 14 2009
next sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
lws, el 14 de diciembre a las 01:06 me escribiste:
 I don't know if I believe this is necesarrily bad.  It's revealing
 some bad coding on your part.
 
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 
 ref Foo getFoo() {} fixes the problem and avoids value-copying for
 no reason to an rvalue that's going to get garbage collected.
If you are using "garbage collected" as a general term to denote "automatic memory reclamation", you are right, but the garbage collector have nothing to do with rvalues, they are allocated in the stack, and they will be "garbage collected" by the compiler when unwinding the stack, not the GC. I don't know if this is relevant to the discussion, I'm clarifying, just in case :)
 On 2009-12-12 07:14:50 -0800, dsimcha <dsimcha yahoo.com> said:
 
I've noticed that, for DMD 2.037, we've started mandating that the input
parameter for struct opEquals be const ref T.  This seemed like a good idea
initially, but it creates the horribly leaky abstraction that the right-hand
argument to opEquals can't be an rvalue.  Example:

struct Foo {
    bool opEquals(const ref Foo rhs) const {  // Only signature
                                              // that compiles.
        return true;
    }
}

Foo getFoo() {
    return Foo();
}

void main() {
    Foo foo = getFoo();
    bool isEqual = foo == getFoo();
}

Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
const bool(Foo rhs)

Will this be getting fixed witht he new operator overloading?
-- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- I would drape myself in velvet if it were socially acceptable. -- George Constanza
Dec 14 2009
parent reply lws <invalid email.com> writes:
Unless I'm confused, classses are ALWAYS heap (unless explicitely 
scoped) objects in D.   The garbage collector is the one that is tasked 
with reclaiming their memory and calling their deconstructor.

If you want the semantics you're talking about Foo would need to be a 
"scoped Foo foo = new Foo();"

I could be wrong, I haven't had a change to touch D in about a year 
though, some stuff could have changed.

 (And please don't top quote, argh! ;))

-SC

On 2009-12-14 05:47:44 -0800, Leandro Lucarella <llucax gmail.com> said:

 lws, el 14 de diciembre a las 01:06 me escribiste:
 I don't know if I believe this is necesarrily bad.  It's revealing
 some bad coding on your part.
 
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 
 ref Foo getFoo() {} fixes the problem and avoids value-copying for
 no reason to an rvalue that's going to get garbage collected.
If you are using "garbage collected" as a general term to denote "automatic memory reclamation", you are right, but the garbage collector have nothing to do with rvalues, they are allocated in the stack, and they will be "garbage collected" by the compiler when unwinding the stack, not the GC. I don't know if this is relevant to the discussion, I'm clarifying, just in case :)
 On 2009-12-12 07:14:50 -0800, dsimcha <dsimcha yahoo.com> said:
 
 I've noticed that, for DMD 2.037, we've started mandating that the input
 parameter for struct opEquals be const ref T.  This seemed like a good idea
 initially, but it creates the horribly leaky abstraction that the right-hand
 argument to opEquals can't be an rvalue.  Example:
 
 struct Foo {
 bool opEquals(const ref Foo rhs) const {  // Only signature
 // that compiles.
 return true;
 }
 }
 
 Foo getFoo() {
 return Foo();
 }
 
 void main() {
 Foo foo = getFoo();
 bool isEqual = foo == getFoo();
 }
 
 Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
 const bool(Foo rhs)
 
 Will this be getting fixed witht he new operator overloading?
Dec 14 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
lws, el 14 de diciembre a las 08:31 me escribiste:
 Unless I'm confused, classses are ALWAYS heap (unless explicitely
 scoped) objects in D.
Yes, and classes are always references too. But that's not even the point, the point is, the example was a struct :)
struct Foo {
bool opEquals(const ref Foo rhs) const {  // Only signature
// that compiles.
return true;
}
}

Foo getFoo() {
return Foo();
}

void main() {
Foo foo = getFoo();
bool isEqual = foo == getFoo();
}

Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
const bool(Foo rhs)

Will this be getting fixed witht he new operator overloading?
[snip]
 (And please don't top quote, argh! ;))
!!! -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Every day 21 new born babies will be given to the wrong parents
Dec 14 2009
parent lws <invalid email.com> writes:
On 2009-12-14 11:04:53 -0800, Leandro Lucarella <llucax gmail.com> said:

 lws, el 14 de diciembre a las 08:31 me escribiste:
 Unless I'm confused, classses are ALWAYS heap (unless explicitely
 scoped) objects in D.
Yes, and classes are always references too. But that's not even the point, the point is, the example was a struct :)
Even if they are always references, they behave as pass-by-value types. Also, excuse me I read struct as class for some reason earlier. (*doh!*) This error-that-should-be-warning is for both types though.
 
 struct Foo {
 bool opEquals(const ref Foo rhs) const {  // Only signature
 // that compiles.
 return true;
 }
 }
 
 Foo getFoo() {
 return Foo();
 }
 
 void main() {
 Foo foo = getFoo();
 bool isEqual = foo == getFoo();
 }
 
 Error:  Foo.opEquals type signature should be const bool(ref const(Foo)) not
 const bool(Foo rhs)
 
 Will this be getting fixed witht he new operator overloading?
[snip]
 (And please don't top quote, argh! ;))
!!!
Dec 14 2009
prev sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from lws (invalid email.com)'s article
 I don't know if I believe this is necesarrily bad.  It's revealing some
 bad coding on your part.
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 ref Foo getFoo() {} fixes the problem and avoids value-copying for no
 reason to an rvalue that's going to get garbage collected.
1. This was in DFL's code, not stuff I wrote. 2. It was a small struct that was cheap to copy, not a class. 3. At any rate, the inconsistency with builtins is inexcusable.
Dec 14 2009
parent reply lws <invalid email.com> writes:
On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:

 == Quote from lws (invalid email.com)'s article
 I don't know if I believe this is necesarrily bad.  It's revealing some
 bad coding on your part.
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 ref Foo getFoo() {} fixes the problem and avoids value-copying for no
 reason to an rvalue that's going to get garbage collected.
1. This was in DFL's code, not stuff I wrote. 2. It was a small struct that was cheap to copy, not a class. 3. At any rate, the inconsistency with builtins is inexcusable.
1. Well, stuff like this is good warning to whomever about the code. Since D is a imperative language, it should at least give you a warning when you're doing something really inefficient that has a boilerplate way of accomplishing it that is much faster. 2. That's odd. structs DO have stack scope in D. Right? It shouldn't even warn in that case. IMHO. 3. For classes, it is consistent with the whole point of the new const stuff and the GC, and the fact that they are heap objects by definition. Classes have always been treated "differently." And I think it's good. Walter has enabled a lot of expressiveness with the syntax in D when compared to C++, and it allows nice warnings when you're doing things you probably shouldn't. Unfortunately, this isn't an error.... Maybe for some reason you REALLY want to be copying around structs and classes.
Dec 14 2009
next sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from lws (invalid email.com)'s article
 On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:
 == Quote from lws (invalid email.com)'s article
 I don't know if I believe this is necesarrily bad.  It's revealing some
 bad coding on your part.
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 ref Foo getFoo() {} fixes the problem and avoids value-copying for no
 reason to an rvalue that's going to get garbage collected.
1. This was in DFL's code, not stuff I wrote. 2. It was a small struct that was cheap to copy, not a class. 3. At any rate, the inconsistency with builtins is inexcusable.
1. Well, stuff like this is good warning to whomever about the code. Since D is a imperative language, it should at least give you a warning when you're doing something really inefficient that has a boilerplate way of accomplishing it that is much faster.
Right, but only if this warning doesn't have too many false positives that are just annoying when the programmer **does** know what he/she is doing. In this case, passing small structs by value isn't inefficient.
 2.  That's odd.  structs DO have stack scope in D.  Right?   It
 shouldn't even warn in that case.  IMHO.
That's my whole point. The behavior is a bug, not a feature.
 3.  For classes, it is consistent with the whole point of the new const
 stuff and the GC, and the fact that they are heap objects by
 definition.   Classes have always been treated "differently."   And I
 think it's good.    Walter has enabled a lot of expressiveness with the
 syntax in D when compared to C++, and it allows nice warnings when
 you're doing things you probably shouldn't.
 Unfortunately, this isn't an error....   Maybe for some reason you
 REALLY want to be copying around structs and classes.
Maybe what we need is for it to be legal to pass rvalues to functions that expect const ref params. If it's an rvalue, just place it on the caller's stack frame implicitly and be done with it.
Dec 14 2009
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 14 Dec 2009 11:44:18 -0500, lws <invalid email.com> wrote:

 On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:

 == Quote from lws (invalid email.com)'s article
 I don't know if I believe this is necesarrily bad.  It's revealing some
 bad coding on your part.
 You shouldn't be doing opEquals with an rvalue of a class.   Make
 getFoo return a reference.
 ref Foo getFoo() {} fixes the problem and avoids value-copying for no
 reason to an rvalue that's going to get garbage collected.
1. This was in DFL's code, not stuff I wrote. 2. It was a small struct that was cheap to copy, not a class. 3. At any rate, the inconsistency with builtins is inexcusable.
1. Well, stuff like this is good warning to whomever about the code. Since D is a imperative language, it should at least give you a warning when you're doing something really inefficient that has a boilerplate way of accomplishing it that is much faster.
It's not faster, it's slower. Passing a reference to an integer or smaller value type is not as efficient passing the value type itself.
 2.  That's odd.  structs DO have stack scope in D.  Right?   It  
 shouldn't even warn in that case.  IMHO.
The issue is that the compiler is incorrectly assuming that it *must* use a const ref form of opEquals for a member when composing an opEquals function for a struct that doesn't provide one. To this end, it always ensures any overload set of opEquals has a const ref form. The premise that const ref is always required is false in some cases, and this is the bug.
 3.  For classes, it is consistent with the whole point of the new const  
 stuff and the GC, and the fact that they are heap objects by  
 definition.   Classes have always been treated "differently."   And I  
 think it's good.    Walter has enabled a lot of expressiveness with the  
 syntax in D when compared to C++, and it allows nice warnings when  
 you're doing things you probably shouldn't.
Classes should be allowed to be passed as const only, no ref. Passing a reference to a class makes no sense since they are references already (Even scoped class variables are references). In fact, const ref makes absolutely no sense for a class, ever.
 Unfortunately, this isn't an error....   Maybe for some reason you  
 REALLY want to be copying around structs and classes.
It is an error, not a warning. The following opEquals functions should always compile: struct S1 { int x; bool opEquals(S1 s) const { return true; } } struct S2 { int *x; bool opEquals(const S2 s) const { return true; } } The following should not necessarily compile: struct Sbad { int *x; bool opEquals(Sbad s) const { return true; } } -Steve
Dec 15 2009
parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:

 On Mon, 14 Dec 2009 11:44:18 -0500, lws <invalid email.com> wrote:

 On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:

 1.  Well, stuff like this is good warning to whomever about the code.    
 Since D is a imperative language, it should at least give you a warning  
 when you're doing something really inefficient that has a boilerplate  
 way of accomplishing it that is much faster.
It's not faster, it's slower. Passing a reference to an integer or smaller value type is not as efficient passing the value type itself.
This is hardly true on modern architectures. I don't have the numbers on it, but even for ints and floats, ref is just as fast (and sometimes faster). -- Simen
Dec 15 2009
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 15 Dec 2009 18:41:30 -0500, Simen kjaeraas  
<simen.kjaras gmail.com> wrote:

 Steven Schveighoffer <schveiguy yahoo.com> wrote:

 On Mon, 14 Dec 2009 11:44:18 -0500, lws <invalid email.com> wrote:

 On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:

 1.  Well, stuff like this is good warning to whomever about the  
 code.   Since D is a imperative language, it should at least give you  
 a warning when you're doing something really inefficient that has a  
 boilerplate way of accomplishing it that is much faster.
It's not faster, it's slower. Passing a reference to an integer or smaller value type is not as efficient passing the value type itself.
This is hardly true on modern architectures. I don't have the numbers on it, but even for ints and floats, ref is just as fast (and sometimes faster).
I don't see how this is possible. It's a double-indirection vs. a single indirection when you look at the value. Yes, it might be inlined, making the lookup just as fast as if it were a stack variable, but I don't see how it could be faster. I guess it may be faster if you don't actually use the value in the function. Besides that point, passing ints or floats by value is not "something really inefficient" that should be considered a compiler error. -Steve
Dec 15 2009
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Simen kjaeraas wrote:
 Steven Schveighoffer <schveiguy yahoo.com> wrote:
 
 On Mon, 14 Dec 2009 11:44:18 -0500, lws <invalid email.com> wrote:

 On 2009-12-14 07:01:47 -0800, dsimcha <dsimcha yahoo.com> said:

 1.  Well, stuff like this is good warning to whomever about the 
 code.   Since D is a imperative language, it should at least give you 
 a warning when you're doing something really inefficient that has a 
 boilerplate way of accomplishing it that is much faster.
It's not faster, it's slower. Passing a reference to an integer or smaller value type is not as efficient passing the value type itself.
This is hardly true on modern architectures. I don't have the numbers on it, but even for ints and floats, ref is just as fast (and sometimes faster).
To return a reference you must allocate the value. And to use the value you must dereference the reference. All this takes cycles no matter how modern your architecture is.
Dec 15 2009