December 14, 2006
Andrei Alexandrescu (See Website for Email) wrote:
> That makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?

No. The caller and callee don't know about each other.

> What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?

You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee.

This is why a lot of C++ code tends to return references instead of values.
December 14, 2006
Walter Bright wrote:
> BCS wrote:
> 
>> With constructors, it is not only simpler code, but looks like what is happening.
>>
>> struct S
>> {
>>     static S err;
>>     int k, l;
>>
>>     this(int i, int j)
>>     {
>>         k=i;
>>         l=j;
>>
>>         if(!ret.test) this = err;
>>     }
>>
>>     bool test(){...}
>> }
> 
> 
> Assignment to this inside a constructor is a mistake as it breaks the assumptions the language makes about constructors.

What assumptions does it break? This would be valid:

struct S
{
    static S err;
    int k, l;
    this(int i, int j)
    {
        k=i;
        l=j;
        if(!ret.test)
        {
           this.k = err.k;
           this.l = err.l;
        }
    }
    bool test(){...}
}

and as far as I can tell, they are the same.
OK well maybe it should have been written as this:

        if(!ret.test) *this = err;
                   // ^- add this

Either way, I think the original argument still holds. The constructor form still looks more like what is acutely happening, and as a result has less of a "phantom" cost.
December 14, 2006
Walter Bright wrote:
> Andrei Alexandrescu (See Website for Email) wrote:
> 
>> That makes me wonder - if a = b is used without picking up its result (the usual case), and if opAssign() returns an S, will the compiler optimize away the extra copy at zero cost?
> 
> 
> No. The caller and callee don't know about each other.
> 
>> What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?
> 
> 
> You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee.
> 
> This is why a lot of C++ code tends to return references instead of values.

I guess compiling internally two versions, one that returns S and one that returns void, might be worth looking into. Because right now user code for opAssign() can't be politically correct and efficient at the same time.

Here's a better alternative:

Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform:

a = b;

into:

(a.opAssign(b), a);

with the mention that a only gets evaluated once.

This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made.


Andrei
December 14, 2006
Let's not forget that the members of a struct can be initialized "inline":

struct X {
  int a = 2;
}

This makes the lack of constructors for structs a lot less significant.

Additionally, destructors for structs are primarily used for wrappers, RAII, and let's not forget scope(...), which is much better suited for RAII than destructors ever were. It makes the lack of destructors for structs a lot less significant..

So, if people want a C++-looking initialize function, they can use static opCall, and it'll look like a call to a constructor (ie. having the same name as the struct).

L.


December 14, 2006
"Alexander Panek" <a.panek@brainsware.org> wrote in message news:eln5td$2pta$1@digitaldaemon.com...
> Are you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do you mean something /similar/ to C# + .NET? If so, I agree. Something like that has to be in D. :) I hope this will pop up

We had one, once : ( but then the guy's HDD crashed... Sad, sad story.

L.


December 14, 2006
BCS wrote:
> OK well maybe it should have been written as this:
> 
>         if(!ret.test) *this = err;
>                    // ^- add this

That's completely different <g>.

> Either way, I think the original argument still holds. The constructor form still looks more like what is acutely happening, and as a result has less of a "phantom" cost.
December 14, 2006
Andrei Alexandrescu (See Website For Email) wrote:
> I guess compiling internally two versions, one that returns S and one that returns void, might be worth looking into. Because right now user code for opAssign() can't be politically correct and efficient at the same time.
> 
> Here's a better alternative:
> 
> Require opAssign() to always return void and have the compiler return a reference to the left-hand side. That is, transform:
> 
> a = b;
> 
> into:
> 
> (a.opAssign(b), a);
> 
> with the mention that a only gets evaluated once.
> 
> This way assignment _always_ returns its left-hand side and user code cannot subvert that behavior. Also the code will be efficient because no more spurious copies are being made.

It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to:

	*(a.opAssign(b))

December 14, 2006
== Quote from Andrei Alexandrescu (See Website For Email)
(SeeWebsiteForEmail@erdani.org)'s article
> Walter Bright wrote:
> > Andrei Alexandrescu (See Website for Email) wrote:
> >
> >> That makes me wonder - if a = b is used without picking up its result
> >> (the usual case), and if opAssign() returns an S, will the compiler
> >> optimize away the extra copy at zero cost?
> >
> >
> > No. The caller and callee don't know about each other.
> >
> >> What I think happens is that the function will take a pointer to the destination which is zero, so a comparison will be made. But perhaps the optimizer will take care of eliminating that?
> >
> >
> > You could have a null pointer passed to the return result, but that would entail an extra check on the part of the callee.
> >
> > This is why a lot of C++ code tends to return references instead of values.
> I guess compiling internally two versions, one that returns S and one
> that returns void, might be worth looking into. Because right now user
> code for opAssign() can't be politically correct and efficient at the
> same time.
> Here's a better alternative:
> Require opAssign() to always return void and have the compiler return a
> reference to the left-hand side. That is, transform:
> a = b;
> into:
> (a.opAssign(b), a);
> with the mention that a only gets evaluated once.
> This way assignment _always_ returns its left-hand side and user code
> cannot subvert that behavior. Also the code will be efficient because no
> more spurious copies are being made.
> Andrei

I wonder, are there cases where assignment should not return 'this'?

What I'm thinking of is something like:

class StringExpression {
  ... holds equations between strings
};

class String {
   // wrapper around char[]
   StringExpression opCat(String x);
   StringExpression opCat(StringExpression x);
};

This 'expression-class' stuff is done more commonly with matrices, where the programmer is trying to to combine multiplies and adds via some optimization rules.

Since C++ at least is flexible (syntax wise at least) on operator inputs and outputs, I have to wonder if there are really consequences for not following those expectations here.

Imagine a string expression like this:

a = b ~ (c = d ~ e);

Now if I can do "StringExpression String::opAssign(...)", this could maybe be
rewritten (code not shown...) to result in something like this:

a = b ~ d ~ e;
c = a[b.length..a.length];

Not that this is worth the effort or that the language should do this with strings, but as a programmer, can I reasonably do this kind of trick with operator return values or am I just digging a hole for myself?  (I realize that even if useful it may not be worthwhile.)

Kevin
December 14, 2006
Walter Bright wrote:
> It is possible to force the return type of opAssign. But I'd suggest making it an S*, with the rewrite to:
> 
>     *(a.opAssign(b))
> 

class C {
  C* opAssign(...) {...}
}
struct S {
  S* opAssign(...) {...}
}

Seriously?? Ugh.

Why not just "C opAssign" and "S opAssign"? In the opCall discussion you said yourself that the return value will be optimized.

Am I missing something?

L.
December 14, 2006
Lionello Lunesu wrote:
> "Alexander Panek" <a.panek@brainsware.org> wrote in message news:eln5td$2pta$1@digitaldaemon.com...
>> Are you, actually, talking about a D -> MSIL compiler, so it can be run inside the VM and a .NET implementation in D, for that purpose? Or do you mean something /similar/ to C# + .NET? If so, I agree. Something like that has to be in D. :) I hope this will pop up
> 
> We had one, once : ( but then the guy's HDD crashed... Sad, sad story.
> 
> L. 
> 
> 

https://mywebspace.wisc.edu/daaugustine/web/d/

This is all that remains of all his work, so far as I can tell.  Some day it would be nice if http://www.scratch-ware.net/dfiles/ suddenly existed again.  Oh well.

-- Chris Nicholson-Sauls