www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - opStar

reply "Janice Caron" <caron800 googlemail.com> writes:
Since D lets us write p.member instead of (*p).member or p->member,
where p is pointer

...and since we now have opStar() to overload *p

...does that mean that if a class A implements opStar(), and p is an
instance of class A, then we get to write p.member to mean
(*p).member? (Assuming no name clashes with A.member, of course)

If not, is this sugar planned for the future?

opStar() is only partway towards the ability to make
pointer-like-objects. If we're going that way, let's go all the way.
Nov 10 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 Since D lets us write p.member instead of (*p).member or p->member,
 where p is pointer
 
 ...and since we now have opStar() to overload *p
 
 ...does that mean that if a class A implements opStar(), and p is an
 instance of class A, then we get to write p.member to mean
 (*p).member? (Assuming no name clashes with A.member, of course)
 
 If not, is this sugar planned for the future?

No. Please no!
 
 opStar() is only partway towards the ability to make
 pointer-like-objects. If we're going that way, let's go all the way.

Nov 10 2007
next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Janice Caron schrieb:
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.
 
 What's opStar for, if not to implement iterators?

i think the same way as in c++ - for example vector class with multiplication operator do not missunderstand the syntax "*e" wrong a * b --> *b == *e
Nov 10 2007
parent dennis luehring <dl.soluz gmx.net> writes:
dennis luehring schrieb:
 Janice Caron schrieb:
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.

 What's opStar for, if not to implement iterators?

i think the same way as in c++ - for example vector class with multiplication operator do not missunderstand the syntax "*e" wrong a * b --> *b == *e

Nov 10 2007
prev sibling next sibling parent reply Bruce Adams <tortoise_74 yeah.woo.co.uk> writes:
Janice Caron Wrote:

 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.
 
 What's opStar for, if not to implement iterators?

Think about what your asking for for a minute though. If p.member was an alias for (*p).member how would you be able to access any p.member in order to implement *p in the first place. The idea doesn't make sense. "Please god no!" was something of an understatement if you ask me. I think this is one reason why C++ has . & -> and you can never overload . Smart pointers will have to be done a different (and hopefully better) way in D.
Nov 10 2007
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-10 09:27:30 -0500, "Janice Caron" <caron800 googlemail.com> said:

 Smart pointers will have to be done a different (and hopefully better)
 way in D.

Well here's to that. Looking forward to finding out what it is.

a.opStar.b But seriously, operator overloading's goal is to allow the programmer to use a consistent syntax for native and non-native types. It seem to me that opStar is pretty limited compared to other operators which can be overriden. Not only it doesn't allow you to use a simple syntax for calling aggregate's members -- as in `a.b` instead of `(*a).b` -- but since D doesn't have a reference type as you have in C++, implementing something as simple as `*a = b` is going to be pretty complicated for any non-object value (objects are reference types in D). So, I'm left wondering about why this operator was made overridable in the first place if it's so limited. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 10 2007
next sibling parent Xinok <xnknet gmail.com> writes:
Janice Caron wrote:
 Maybe Walter will add opStarAssign()?
 
 (Nice call on the name, too!)

I don't think that would work. Unlike "n[]=", "*n=" is not it's own operator, it's two separate operators. I was thinking, what if the return type is a pointer, then the compiler automatically dereferences the pointer? Take for example: class N{ int v; int* opStar(){ return &v; } } N obj = new N; *obj = 10; What if the compiler rewrites the last statement as: *obj.opStar() = 10; This wouldn't be the only operator with unusual behavior. D already has opEquals and opCmp, where the compiler will rewrite the expression similarly to this.
Nov 10 2007
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-10 10:34:38 -0500, "Janice Caron" <caron800 googlemail.com> said:

 On 11/10/07, Michel Fortin <michel.fortin michelf.com> wrote:
 implementing
 something as simple as `*a = b` is going to be pretty complicated for
 any non-object value

Maybe Walter will add opStarAssign()?

And then opStarAddAssign, opStarDivAssign, opStarShlAssign, opStarCatAssign... no, that doesn't sound right. Beside, if you want real pointer semantics, you'll want to overload the `is` operator too... -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 10 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Michel Fortin wrote:
 On 2007-11-10 09:27:30 -0500, "Janice Caron" <caron800 googlemail.com> 
 said:
 
 Smart pointers will have to be done a different (and hopefully better)
 way in D.

Well here's to that. Looking forward to finding out what it is.

a.opStar.b But seriously, operator overloading's goal is to allow the programmer to use a consistent syntax for native and non-native types.

I think that's an excellent point. If you have to use unnatural syntax to use it (i.e. (*x).member) then you might as well make it a normal member property. I'd much rather have x.val.member all over my code than (*x).member, and in fact that's the way I've translated the operator* in all the iterators i've ported from C++ to D. Granted, having opStar/opDeref would make at least some syntaxes work for both pointers and smart pointers, but it would really suck if all code for handling iterators generically (like a std.algorithm module) were forced to use that cumbersome syntax in order to be generic. D has been striving for some time to make writing templates as simple and straightforward as possible. It almost seems to me that if (*x).member and x.member are going to have different meanings, then x.member should be the one that gets overridden by opStar. Except that it makes no sense if you deconstruct it because of the order of evaluation forced by the parentheses. So maybe there needs to be a completely different way to do the operation of "I want to dereference the raw POINTER darnit! then access a member". If you don't use that special one then what you get would be opStar's version. --bb
Nov 10 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.

Not really. You cannot overload references in C++, either.
 What's opStar for, if not to implement iterators?

For iterators. Should a class be used as an iterator? I can't see how that would make sense anyway.
Nov 10 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Janice Caron wrote:
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.

Not really. You cannot overload references in C++, either.
 What's opStar for, if not to implement iterators?

For iterators. Should a class be used as an iterator? I can't see how that would make sense anyway.

If you're not going to be able to access members and such via the opStar, why not just have iterators use a convention like p.val to access the value pointed too, rather than *p. Why does it need to be an operator? --bb
Nov 10 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 If you're not going to be able to access members and such via the 
 opStar, why not just have iterators use a convention like p.val to 
 access the value pointed too, rather than *p.  Why does it need to be an 
 operator?

Because raw pointers don't have a .value property, and can't because then you'd have problems with pointers to structs that have a value member.
Nov 11 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 If you're not going to be able to access members and such via the 
 opStar, why not just have iterators use a convention like p.val to 
 access the value pointed too, rather than *p.  Why does it need to be 
 an operator?

Because raw pointers don't have a .value property, and can't because then you'd have problems with pointers to structs that have a value member.

So what about smart pointers? Do you really just expect everyone to suck it up and get used to writing (*x).foo? --bb
Nov 11 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 If you're not going to be able to access members and such via the 
 opStar, why not just have iterators use a convention like p.val to 
 access the value pointed too, rather than *p.  Why does it need to be 
 an operator?

Because raw pointers don't have a .value property, and can't because then you'd have problems with pointers to structs that have a value member.

So what about smart pointers? Do you really just expect everyone to suck it up and get used to writing (*x).foo?

There are other ways to do smart pointers. I happen to strongly dislike the C++ way of doing it.
Nov 11 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On Nov 12, 2007 2:17 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 There are other ways to do smart pointers. I happen to strongly dislike
 the C++ way of doing it.

Great! So do I! Which leads me to wonder why you gave us opStar(), which seems to me to be /exactly/ the C++ way (except without the convenience of ->)

It's not for smart pointers. It's for iterators. To do an iterator, you need 4 operations: 1) copy 2) increment 3) compare for equality 4) get the value The first 3 are already covered by operator overloading, so we need one for the last. You could do these through member functions, but then you couldn't use a pointer as an iterator.
Nov 12 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On Nov 12, 2007 8:50 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 1) copy
 2) increment
 3) compare for equality
 4) get the value

You missed 5) assignment

I think (1) covers that.
 (And also decrement, but I guess increment and decrement are both just
 special cases of adding or subtracting an integer)

Yes. Also, some iterators (like a linked list iterator) cannot go backwards. But all must be able to go forwards.
 You also haven't addressed the point that since no function, opStar
 included, can return a reference to a struct, then we are forced to
 return a pointer to a struct, so now your iterator behaves not like a
 pointer, but a pointer to a pointer.

Iterators should be very small value types. I don't see why one would be returning a reference to an iterator.
Nov 12 2007
next sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Janice Caron wrote:
 
 You also haven't addressed the point that since no function, opStar
 included, can return a reference to a struct, then we are forced to
 return a pointer to a struct, so now your iterator behaves not like a
 pointer, but a pointer to a pointer.

Iterators should be very small value types. I don't see why one would be returning a reference to an iterator.

I thought Janice meant an iterator across a range of structs. Sean
Nov 12 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 11/12/07, Walter Bright <newshound1 digitalmars.com> wrote:
 
 If the latter, then p is behaving like a pointer-to-a-pointer, not a
 pointer, and now you'd have to (**p), not (*p), to reach an actual
 VeryLargeStruct, and you'd have to write (**p).m to access the members
 thereof.

Well, actually (*p).m would work too even if (*p) returns a pointer. But yeh, if the VeryLargeStruct had an opAdd you wanted to invoke then you'd need (**p)+5 But anyway Walter has said that returnable references are coming at some point. So maybe the answer is that you use the pointer kludge until that happens. The problem is not unique to opStar. opIndex also has that issue. p[0].m is presumably doing the same thing opStar would do with VeryLargeStruct. --bb
Nov 12 2007
prev sibling next sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Janice Caron wrote:
 On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because raw pointers don't have a .value property

[random rant deleted]

Sigh. Walter Bright wrote:
 Because raw pointers don't have a .value property, /and can't/ because
 then you'd have problems with pointers to structs that have a value
 member.

Nov 11 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Janice Caron wrote:
 On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because raw pointers don't have a .value property

[random rant deleted]

Sigh. Walter Bright wrote:
 Because raw pointers don't have a .value property, /and can't/ because
 then you'd have problems with pointers to structs that have a value
 member.


Well, Janice's point still stands. If that were a real justification then we should be jumping up and down about the fact that ptr.sizeof returns 4 instead of the size of the thing it points to. Walter should just address the real issue. Are smart pointers in D really going to require (*p).member? Or is there something else in the works for transparent overriding of member access? --bb
Nov 11 2007
parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bill Baxter wrote:
 Well, Janice's point still stands.  If that were a real justification 
 then we should be jumping up and down about the fact that ptr.sizeof 
 returns 4 instead of the size of the thing it points to.

IMHO it's not quite the same. Usually you wouldn't create structs with a member called "sizeof", whereas a member called "val" or "value" is a rather common beast. I admit this could be circumvented by using a less common property name like "deref", but that was not my point. What I sighed about was the apparent lack of reading before ranting.
 Walter should just address the real issue.  Are smart pointers in D 
 really going to require (*p).member?  Or is there something else in the 
 works for transparent overriding of member access?

Believe me, I am also not happy about "(*p).member". I wonder if getting back -> for that purpose (as has been suggested) might be viable. Would make "p->member". Or maybe "p.deref.member" if you need, but please not "p.value.member", I think that might break a lot of code. Regards, Frank
Nov 11 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Bill Baxter wrote:
 Well, Janice's point still stands.  If that were a real justification 
 then we should be jumping up and down about the fact that ptr.sizeof 
 returns 4 instead of the size of the thing it points to.

IMHO it's not quite the same. Usually you wouldn't create structs with a member called "sizeof", whereas a member called "val" or "value" is a rather common beast. I admit this could be circumvented by using a less common property name like "deref", but that was not my point. What I sighed about was the apparent lack of reading before ranting.
 Walter should just address the real issue.  Are smart pointers in D 
 really going to require (*p).member?  Or is there something else in 
 the works for transparent overriding of member access?

Believe me, I am also not happy about "(*p).member". I wonder if getting back -> for that purpose (as has been suggested) might be viable. Would make "p->member". Or maybe "p.deref.member" if you need, but please not "p.value.member", I think that might break a lot of code.

Or instead of .deref you could call the property something like, oh i dunno, "opStar". ;) --bb
Nov 11 2007
parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bill Baxter wrote:
 Or instead of .deref you could call the property something like, oh i 
 dunno, "opStar".  ;)

Now, there's a novel thought! ;) BTW might it be that this is an example how Perly syntax kluges like automatic dereferencing of *struct members can get you into trouble? Regards, Frank
Nov 11 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Bill Baxter wrote:
 Or instead of .deref you could call the property something like, oh i 
 dunno, "opStar".  ;)

Now, there's a novel thought! ;) BTW might it be that this is an example how Perly syntax kluges like automatic dereferencing of *struct members can get you into trouble?

Or just how making special cases in general gets you in trouble. But actually I don't think there's that much trouble here. First, let's call it opDeref because, to quote Walter re opAdd: """ The reasons for "opAdd" instead of "operator+" are: ... 3) it encourages the use of operating overloading for arithmetic purposes, rather than "parse this predicate once", which happens with C++ operator overloading. """ http://lists.puremagic.com/pipermail/digitalmars-d/2006-October/009478.html Second, the only thing that makes sense if you're going to have smart pointers is to make them act in every way possible like regular pointers. Iterators are just the underevolved Neanderthal ancestors of smart pointers, but they're still smart pointers of a sort. So we have to make "x.member" call "x.opDeref.member" if x is something with an opDeref. Third: we need to figure out some way to actually get at x's own members. I think there are a number of ways you could do that. 1) In D, the magic pointer dereferencing will only dereference one level. So that means if x is a struct with opDeref, then (&x).member will give you a member of x itself, not of the opDeref. 2) Make a special property that if used disables the opDeref. Maybe even x.this.member could be used for that. Actually, I think because of 1) that should work automatically if x is a struct. 3) Introduce an alternate member access syntax for 'raw' member access. Like x:member, x::member, x->member, x..member. Option 1) seems the most reasonable to start with, since it shouldn't actually require doing anything. --bb
Nov 11 2007
next sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bill Baxter wrote:
 First, let's call it opDeref because, to quote Walter re opAdd:

++votes
 3) Introduce an alternate member access syntax for 'raw' member access. 
  Like x:member, x::member, x->member,  x..member.

Yup, I'd like "->" for "dumb" dereferencing and "." for "smart". Also, Janices idea to have "." do multi-level dereferencing is surely worth a closer look. If it wasn't for the large user base, I'd say "It sounds good, let's try and see what comes of it!"... =) Regards, Frank
Nov 11 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
0ffh wrote:
 Bill Baxter wrote:
 First, let's call it opDeref because, to quote Walter re opAdd:

++votes
 3) Introduce an alternate member access syntax for 'raw' member 
 access.  Like x:member, x::member, x->member,  x..member.

Yup, I'd like "->" for "dumb" dereferencing and "." for "smart". Also, Janices idea to have "." do multi-level dereferencing is surely worth a closer look. If it wasn't for the large user base, I'd say "It sounds good, let's try and see what comes of it!"... =)

Well, if polls can be trusted, the 2.0 user base is about 1/4 the size of 1.x user base, and the 2.0 *code* base is probably more like 1/100 or less. Anyway, I'd like to have "." do multi-level dereferencing too. I just suspected that wasn't on the table as it's been discussed before. But maybe there's still hope there. Also I noticed that if smart member access is going to be the default then it seems necessary to make clear it doesn't apply to operators. That is, ++p may be equivalent to p.opPreIncr generally, but that doesn't mean that ++p will call p.opDeref.opPreIncr. For that you'll still need the explicit dereference: ++(*p). Without that rule iterators will look pretty ugly. --bb
Nov 11 2007
parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bill Baxter wrote:
 Also I noticed that if smart member access is going to be the default 
 then it seems necessary to make clear it doesn't apply to operators. 
 That is, ++p may be equivalent to p.opPreIncr generally, but that 
 doesn't mean that ++p will call p.opDeref.opPreIncr.  For that you'll 
 still need the explicit dereference: ++(*p).
 
 Without that rule iterators will look pretty ugly.

Yup, I'd also like my pointer arithmetics to keep working... Regards, Frank
Nov 11 2007
prev sibling parent guslay <guslay gmail.com> writes:
If you think of classes with opStar() operator as classes that have "Proxy"
behavior, it makes a lot of sense to have object->member() mean "forward the
invocation to another object", the object returned by opStar().


Note that it is not the same as -> in the C world, which means "call member
through pointer". Here -> is not applied to a pointer (not on the surface at
least), its applied to the "proxy" object.


Smart pointers and iterators, unlike straight pointers, cannot be unambiguously
assimilated to the object referred to. They are wrappers to an object. Blurring
that line with a of the "." that is sometime dereferenced, sometime not, seems
a little bit tricky to me.




Bill Baxter Wrote:

 0ffh wrote:
 Bill Baxter wrote:
 Or instead of .deref you could call the property something like, oh i 
 dunno, "opStar".  ;)

Now, there's a novel thought! ;) BTW might it be that this is an example how Perly syntax kluges like automatic dereferencing of *struct members can get you into trouble?

Or just how making special cases in general gets you in trouble. But actually I don't think there's that much trouble here. First, let's call it opDeref because, to quote Walter re opAdd: """ The reasons for "opAdd" instead of "operator+" are: ... 3) it encourages the use of operating overloading for arithmetic purposes, rather than "parse this predicate once", which happens with C++ operator overloading. """ http://lists.puremagic.com/pipermail/digitalmars-d/2006-October/009478.html Second, the only thing that makes sense if you're going to have smart pointers is to make them act in every way possible like regular pointers. Iterators are just the underevolved Neanderthal ancestors of smart pointers, but they're still smart pointers of a sort. So we have to make "x.member" call "x.opDeref.member" if x is something with an opDeref. Third: we need to figure out some way to actually get at x's own members. I think there are a number of ways you could do that. 1) In D, the magic pointer dereferencing will only dereference one level. So that means if x is a struct with opDeref, then (&x).member will give you a member of x itself, not of the opDeref. 2) Make a special property that if used disables the opDeref. Maybe even x.this.member could be used for that. Actually, I think because of 1) that should work automatically if x is a struct. 3) Introduce an alternate member access syntax for 'raw' member access. Like x:member, x::member, x->member, x..member. Option 1) seems the most reasonable to start with, since it shouldn't actually require doing anything. --bb

Nov 11 2007
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because raw pointers don't have a .value property

It's your language, so you could give raw pointers whatever properties you wanted. They already have .sizeof and .init and .mangleof. If you wanted to, you could give pointers a .value property. Just saying "they don't have that property" is a silly argument for someone with your godlike powers over the language.

Adding new properties is a pretty disruptive thing to do, especially common names.
Nov 11 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Bill Baxter wrote:
 If you're not going to be able to access members and such via the 
 opStar, why not just have iterators use a convention like p.val to 
 access the value pointed too, rather than *p.  Why does it need to be 
 an operator?

Because raw pointers don't have a .value property, and can't because then you'd have problems with pointers to structs that have a value member.

Sure. But how much value is there in supporting pointers as iterators? Even the C++ community has moved away from this idea, although the syntax still supports it. Sean
Nov 12 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Bill Baxter wrote:
 If you're not going to be able to access members and such via the 
 opStar, why not just have iterators use a convention like p.val to 
 access the value pointed too, rather than *p.  Why does it need to be 
 an operator?

Because raw pointers don't have a .value property, and can't because then you'd have problems with pointers to structs that have a value member.

Sure. But how much value is there in supporting pointers as iterators? Even the C++ community has moved away from this idea, although the syntax still supports it.

True, true. I almost never see raw pointers used as iterators in C++. And you can always adapt a raw pointer with a struct that implements the iterator interface. like pointer_iter(pBegin, pEnd). --bb
Nov 12 2007
prev sibling parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Sean Kelly wrote:
 [...]
 Sure.  But how much value is there in supporting pointers as iterators? 
  Even the C++ community has moved away from this idea, although the 
 syntax still supports it.

Actually, I use array-iterators all the time in C++ and it works great. Lots of people I know do as well. And being that D is practically a vector language, it would be an Unpardonable Sin for it not to support array-iterators. Dave
Nov 12 2007
parent reply Sean Kelly <sean f4.ca> writes:
David B. Held wrote:
 Sean Kelly wrote:
 [...]
 Sure.  But how much value is there in supporting pointers as 
 iterators?  Even the C++ community has moved away from this idea, 
 although the syntax still supports it.

Actually, I use array-iterators all the time in C++ and it works great. Lots of people I know do as well. And being that D is practically a vector language, it would be an Unpardonable Sin for it not to support array-iterators.

Well sure, but do those iterators have to be pointers, or can they be a struct which contains a pointer? The size isn't any different, and structs can provide a lot of added value in terms of optional range checking and such. I don't know of a single STL implementation that uses raw pointers for vector iterators any more, for example. Sean
Nov 12 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Sean Kelly wrote:
 David B. Held wrote:
 Sean Kelly wrote:
 [...]
 Sure.  But how much value is there in supporting pointers as 
 iterators?  Even the C++ community has moved away from this idea, 
 although the syntax still supports it.

Actually, I use array-iterators all the time in C++ and it works great. Lots of people I know do as well. And being that D is practically a vector language, it would be an Unpardonable Sin for it not to support array-iterators.

Well sure, but do those iterators have to be pointers, or can they be a struct which contains a pointer? The size isn't any different, and structs can provide a lot of added value in terms of optional range checking and such. I don't know of a single STL implementation that uses raw pointers for vector iterators any more, for example.

int size[3] = { length, width, height }; std::sort(size + 0, size + 3); I do lots of little stuff like this, which is very convenient. Dave
Nov 12 2007
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
Janice Caron wrote:
 On Nov 13, 2007 6:17 AM, David B. Held <dheld codelogicconsulting.com> wrote:
 int size[3] = { length, width, height };
 std::sort(size + 0, size + 3);

 I do lots of little stuff like this, which is very convenient.

In D: size.sort; Pointers not needed.

This presumes that the builtin sort knows the ordering I want to use. Let's try this: Customer[] list = { Jim, Bob, Fred }; list.sort; Does list get sorted by first name, last name, phone number, SSN, favorite color, or uncle's friend's dog's favorite park? If I want to define a custom sort that takes an ordering, I can't use the builtin. Now, in D, arrays are first-class objects, so I would just take an array directly...unless I wrote a generic sort algorithm that took iterators instead. Then I would rather pass array-iterators to the sort algorithm. Dave
Nov 13 2007
parent reply Sean Kelly <sean f4.ca> writes:
David B. Held wrote:
 Janice Caron wrote:
 On Nov 13, 2007 6:17 AM, David B. Held <dheld codelogicconsulting.com> 
 wrote:
 int size[3] = { length, width, height };
 std::sort(size + 0, size + 3);

 I do lots of little stuff like this, which is very convenient.

In D: size.sort; Pointers not needed.

This presumes that the builtin sort knows the ordering I want to use. Let's try this: Customer[] list = { Jim, Bob, Fred }; list.sort; Does list get sorted by first name, last name, phone number, SSN, favorite color, or uncle's friend's dog's favorite park? If I want to define a custom sort that takes an ordering, I can't use the builtin. Now, in D, arrays are first-class objects, so I would just take an array directly...unless I wrote a generic sort algorithm that took iterators instead. Then I would rather pass array-iterators to the sort algorithm.

Tango has a sort routine that accepts a comparator, and it can be called with the exact same syntax as the built-in version. It even works on ranges: list[0 .. 2].sort( &myCmp ); See: http://www.dsource.org/projects/tango/browser/trunk/tango/core/Array.d The great thing about iterators is that they act as a sort of bookmark, so the result of an operation can be stored in a generic manner. But algorithms like sorting, searching, and so on, all operate on a range, and D has these already in the form of slices. Assuming that we had random access iterators as well, I'd use them to define a slice and perform my operation that way rather than pass them in separately. Sean
Nov 13 2007
parent Lutger <lutger.blijdestijn gmail.com> writes:
Sean Kelly wrote:
 
 The great thing about iterators is that they act as a sort of bookmark, 
 so the result of an operation can be stored in a generic manner.  But 
 algorithms like sorting, searching, and so on, all operate on a range, 
 and D has these already in the form of slices.  Assuming that we had 
 random access iterators as well, I'd use them to define a slice and 
 perform my operation that way rather than pass them in separately.
 
 
 Sean

There's a funny little remark in 'The C++ programming language' by Stroustrup about this. He mentions that a lot of algorithms operate on ranges which are just iterator pairs, shows how to define such a type yourself and then apologises to the reader that the STL doesn't include ranges (or sequences as he calls them).
Nov 14 2007
prev sibling next sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Walter Bright Wrote:
 
 What's opStar for, if not to implement iterators?

For iterators. Should a class be used as an iterator? I can't see how that would make sense anyway.

Are we to infer from that that iterators (and possibly smart pointers) might be a new kind of first class entity or did you just mean structs. Regards, Bruce.
Nov 11 2007
prev sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-11 00:42:07 -0500, Walter Bright <newshound1 digitalmars.com> said:

 What's opStar for, if not to implement iterators?

For iterators.

I can't comment much on that since the details are so scarse, but I hope you don't use the same syntax as in C++ for iterators. I've never like much C++ pointer-like iterators, and I don't think working with iterators should feel like we're manipulating pointers. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 11 2007
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 11/11/07, Michel Fortin <michel.fortin michelf.com> wrote:
 On 2007-11-11 00:42:07 -0500, Walter Bright <newshound1 digitalmars.com> said:

 What's opStar for, if not to implement iterators?


like much C++ pointer-like iterators, and I don't think working with iterators should feel like we're manipulating pointers.

Are you aware of any primitive type other than a pointer which takes a unary asterisk operator, for any purpose whatsoever?

Kleene star? If you want to call a regex a primitive type :P
Nov 11 2007
parent "David B. Held" <dheld codelogicconsulting.com> writes:
Christopher Wright wrote:
 Janice Caron wrote:
 On 11/11/07, Michel Fortin <michel.fortin michelf.com> wrote:
 On 2007-11-11 00:42:07 -0500, Walter Bright 
 <newshound1 digitalmars.com> said:

 What's opStar for, if not to implement iterators?


like much C++ pointer-like iterators, and I don't think working with iterators should feel like we're manipulating pointers.

Are you aware of any primitive type other than a pointer which takes a unary asterisk operator, for any purpose whatsoever?

Kleene star? If you want to call a regex a primitive type :P

Technically the Kleene star is a postfix operator, whereas C's pointer dereference operator is prefix. Dave
Nov 11 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Michel Fortin wrote:
 I can't comment much on that since the details are so scarse, but I hope 
 you don't use the same syntax as in C++ for iterators. I've never like 
 much C++ pointer-like iterators, and I don't think working with 
 iterators should feel like we're manipulating pointers.

The thing is that loops are a very fundamental construct, and being to implement them efficiently is of major importance. The pointer idiom has the advantage there.
Nov 11 2007
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-11 14:33:55 -0500, Walter Bright <newshound1 digitalmars.com> said:

 Michel Fortin wrote:
 I can't comment much on that since the details are so scarse, but I 
 hope you don't use the same syntax as in C++ for iterators. I've never 
 like much C++ pointer-like iterators, and I don't think working with 
 iterators should feel like we're manipulating pointers.

The thing is that loops are a very fundamental construct, and being to implement them efficiently is of major importance. The pointer idiom has the advantage there.

Hum, I'm not sure I follow you here. I say I dislike the C++ syntax for iterators (because the syntax make it look like a pointer) and your reply is that pointers are more efficient... Pointer are efficient indeed, but pointers should be an implementation detail that has nothing to do about the syntax for iterators. My point was aboout the syntax, not the implemenation. If you implement the iterator as a struct having a pointer then that's perfect with me. If the iterator has a pointer-like syntax, then it won't be my prefered syntax, but I can digest. If the iterator has only a half-pointer-like syntax -- like needing an explicit parenthesis and dereferenciation asterisk (*a).b to access the members of the pointed-to element instead of the simpler a.b you can use with a real pointers -- then that's downright bad in my opinion. But as I said, I haven't seen what you're planing for iterators, so perhaps it's better than what I'm currently imagining. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 11 2007
parent "David B. Held" <dheld codelogicconsulting.com> writes:
Michel Fortin wrote:
 [...]
 If you implement the iterator as a struct having a pointer then that's 
 perfect with me. If the iterator has a pointer-like syntax, then it 
 won't be my prefered syntax, but I can digest. If the iterator has only 
 a half-pointer-like syntax -- like needing an explicit parenthesis and 
 dereferenciation asterisk (*a).b to access the members of the pointed-to 
 element instead of the simpler a.b you can use with a real pointers -- 
 then that's downright bad in my opinion.
 
 But as I said, I haven't seen what you're planing for iterators, so 
 perhaps it's better than what I'm currently imagining.

One thing that should be considered is that people have gotten along just fine without iterators up until now, so I think it's premature to say that opStar() is going to bring about the Apocalypse. While it's true that an operator-> might be nice, consider that a good deal of the time, Iterators are used in looping constructs, and what does a good D foreach loop look like? foreach (object; container) { object.useWithoutRegardToPointersOrIterators(); } So if you could use most iterators without actually seeing them, does it matter if you have to explicitly say object = *i? You don't have to say it personally. And hopefully, most algorithms that take iterators as arguments will also hide this implementation detail. Granted, when you want to manipulate iterators directly, you will have to treat them like C-style pointers with manual dereference. But I suspect that when people start actually using iterators, this won't be so much of an issue. Dave
Nov 12 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Michel Fortin wrote:
 I can't comment much on that since the details are so scarse, but I 
 hope you don't use the same syntax as in C++ for iterators. I've never 
 like much C++ pointer-like iterators, and I don't think working with 
 iterators should feel like we're manipulating pointers.

The thing is that loops are a very fundamental construct, and being to implement them efficiently is of major importance. The pointer idiom has the advantage there.

How would the pointer idiom have any advantage in terms of performance?? (versus say, structs with hasNext() and next() member functions) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Nov 13 2007
next sibling parent reply 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
Bruno Medeiros wrote:
 How would the pointer idiom have any advantage in terms of performance??
 (versus say, structs with hasNext() and next() member functions)

Let me take a guess, using arrays and foreach as an example instead of general iterables and iteration. Foreach with pointers: struct array(T) { T* first; T* after; } void foo(array!(Bar) baz) { // iterate over the members of baz, and doSomethingWith them // the sourcecode // foreach (elem;baz) // doSomethingWith(elem); // gets translated to something alike for (Bar* elemPtr=baz.first;elemPtr<baz.after;++elemPtr) { doSomethingWith(*elemPtr); } } Using reset(), hasMore() and getNext() (I know this is only one of several possibilities) struct array(T) { // internal data structure irrelevant T reset() { // set internal position to first element } bool hasMore() { // check that position is not beyond end } T getNext() { // return current element and increase position } } function foo(array!(Bar) baz) { // iterate over the members of baz, and doSomethingWith them // the sourcecode // foreach (elem;baz) // doSomethingWith(elem); // gets translated to something alike baz.reset(); while (baz.hasMore()) { Bar elem=baz.getNext(); doSomethingWith(elem); } } Now, where there used to be a few pointer accesses in the other version, we have two function calls per iteration. In the absolute best case (the array does internally work similar to the example above, and functions are inlined) we may get the same performance for both. In all other cases the second option is slower. Regards, Frank
Nov 13 2007
next sibling parent 0ffh <frank frankhirsch.youknow.what.todo.net> writes:
0ffh wrote:
 [lots...]

Um, maybe I was missing the point here. I'm a bit tired, lemme think, maybe I come up with sth better... Regards, Frank
Nov 13 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
0ffh wrote:
 Bruno Medeiros wrote:
  > How would the pointer idiom have any advantage in terms of performance??
  > (versus say, structs with hasNext() and next() member functions)
 
 Let me take a guess, using arrays and foreach as an example instead of
 general iterables and iteration.
 
 Foreach with pointers:
 
   struct array(T)
   {
     T* first;
     T* after;
   }
 
   void foo(array!(Bar) baz)
   {
     // iterate over the members of baz, and doSomethingWith them
     // the sourcecode
     //   foreach (elem;baz)
     //     doSomethingWith(elem);
     // gets translated to something alike
     for (Bar* elemPtr=baz.first;elemPtr<baz.after;++elemPtr)
     {
       doSomethingWith(*elemPtr);
     }
   }
 
 Using reset(), hasMore() and getNext()
 (I know this is only one of several possibilities)
 
   struct array(T)
   {
     // internal data structure irrelevant
     T reset()
     {
       // set internal position to first element
     }
     bool hasMore()
     {
       // check that position is not beyond end
     }
     T getNext()
     {
       // return current element and increase position
     }
   }
 
   function foo(array!(Bar) baz)
   {
     // iterate over the members of baz, and doSomethingWith them
     // the sourcecode
     //   foreach (elem;baz)
     //     doSomethingWith(elem);
     // gets translated to something alike
     baz.reset();
     while (baz.hasMore())
     {
       Bar elem=baz.getNext();
       doSomethingWith(elem);
     }
   }
 
 
 Now, where there used to be a few pointer accesses in the other
 version, we have two function calls per iteration.
 In the absolute best case (the array does internally work similar
 to the example above, and functions are inlined) we may get the same
 performance for both. In all other cases the second option is slower.
 
 Regards, Frank
 

But indeed the iterator version is trivially inlinable in all it's functions, which should make it degenerate in the same code as the array pointers version, so that's why I asked why would the pointer idiom be more efficient. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Nov 14 2007
prev sibling parent BCS <ao pathlink.com> writes:
Reply to Bruno,

 Walter Bright wrote:
 
 Michel Fortin wrote:
 
 I can't comment much on that since the details are so scarse, but I
 hope you don't use the same syntax as in C++ for iterators. I've
 never like much C++ pointer-like iterators, and I don't think
 working with iterators should feel like we're manipulating pointers.
 

to implement them efficiently is of major importance. The pointer idiom has the advantage there.

performance?? (versus say, structs with hasNext() and next() member functions)

I /think/ part of the though is that in template code you want to be able to use pointers and other things with the exact same syntax. You don't want to have to hide the pointers to get them to act like iteraters.
Nov 13 2007
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-11 07:58:03 -0500, "Janice Caron" <caron800 googlemail.com> said:

 On 11/11/07, Michel Fortin <michel.fortin michelf.com> wrote:
 On 2007-11-11 00:42:07 -0500, Walter Bright <newshound1 digitalmars.com> said:
 
 What's opStar for, if not to implement iterators?

For iterators.

I've never like much C++ pointer-like iterators, and I don't think working with iterators should feel like we're manipulating pointers.

Are you aware of any primitive type other than a pointer which takes a unary asterisk operator, for any purpose whatsoever?

Not that I know of. Should there be? I'm pretty sure that opStar, despite its ambiguous naming, aims at being the dereferencation operator. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 11 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
So, we can write p.m for real pointers, but we have to write (*p).m
for classes that implement opStar()? That seems inconsistent.

What's opStar for, if not to implement iterators?
Nov 10 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()?

You can also write (*p).m for pointers :P
 That seems inconsistent.

I think it has to be. If we examine C++, the normal pointer operator -> has no real meaning by default for a class. This allows them to define the operator without possibility of collision. In D, the dot DOES have a meaning, and so you would be overriding that meaning depending on the member variable. To illustrate the point, what if you have a pointer class template, somone uses it, but happens to point to a class that has a member named the same as one of the pointer class' members? Now your pointer class has no value for that specific class type.
 What's opStar for, if not to implement iterators?

You can still implement iterators, even without opStar. With opStar, if you consistently use pointers in the (*p).m fashion, a pointer class can be swapped in. Yeah, it's ugly, but possible :) If you are writing a template function that should take either pointers or pointer-like classes, then write the (*p).m form, and it should work. -Steve
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Bruce Adams <tortoise_74 yeah.woo.co.uk> wrote:
 I think this is one reason why C++ has . & -> and you can never overload .

Historically, C came before C++. In C, a->b was simply an abbreviation for (*a).b, so when C++ came along, there was something to overload. In D, we use a.b as the abbreviation for (*a).b - at least for pointers. But I accept that it probably can't be made to work for non-pointers.
 Smart pointers will have to be done a different (and hopefully better)
 way in D.

Well here's to that. Looking forward to finding out what it is.
Nov 10 2007
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-10 04:21:41 -0500, "Janice Caron" <caron800 googlemail.com> said:

 ...and since we now have opStar() to overload *p

Hum, is it just me or is the name of this operator inconsistant? Aren't operators in D supposed to be named after what they do instead of what they look like in order to discourage programmers from changing the operator's meaning? So why isn't opStar named opDereference, or opDeref for short? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Michel Fortin <michel.fortin michelf.com> wrote:
 implementing
 something as simple as `*a = b` is going to be pretty complicated for
 any non-object value

Maybe Walter will add opStarAssign()? (Nice call on the name, too!)
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Michel Fortin <michel.fortin michelf.com> wrote:
 But seriously, operator overloading's goal is to allow the programmer
 to use a consistent syntax for native and non-native types. It seem to
 me that opStar is pretty limited compared to other operators which can
 be overriden. Not only it doesn't allow you to use a simple syntax for
 calling aggregate's members -- as in `a.b` instead of `(*a).b` -- but
 since D doesn't have a reference type as you have in C++, implementing
 something as simple as `*a = b` is going to be pretty complicated for
 any non-object value (objects are reference types in D).

Indeed. And given that D has always (until now) discouraged the use of pointers, and encouraged things like arrays and the "ref" parameter storage class in their place, I have never actually been tempted to even /use/ pointers, let alone emulate them. It's always been easy enough to mimic a pointer as a dynamic array with one element (because of how arrays are implemented in D). Smart arrays have always been overloadable, and a[0] = b; is just as workable as b = a[0]; The same cannot be said of *a. Also a[0].member is no more keystrokes than (*a).member (and doesn't involve the shift key).
 So, I'm left wondering about why this operator was made overridable in
 the first place if it's so limited.

Yeah - I'd like to know that too. I'm not knocking it, mind - I just haven't grasped the intent yet.
Nov 10 2007
prev sibling next sibling parent "Chris Miller" <chris dprogramming.com> writes:
On Sat, 10 Nov 2007 04:21:41 -0500, Janice Caron <caron800 googlemail.com>  
wrote:

 Since D lets us write p.member instead of (*p).member or p->member,
 where p is pointer

 ...and since we now have opStar() to overload *p

 ...does that mean that if a class A implements opStar(), and p is an
 instance of class A, then we get to write p.member to mean
 (*p).member? (Assuming no name clashes with A.member, of course)

 If not, is this sugar planned for the future?

 opStar() is only partway towards the ability to make
 pointer-like-objects. If we're going that way, let's go all the way.

What if, instead, D adds -> back but only as a user-defined operator. It could mean redirected access. Instead of (*p).member, you could use p->member and know it's not pointer access but object redirected access.
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Michel Fortin <michel.fortin michelf.com> wrote:
 Maybe Walter will add opStarAssign()?

And then opStarAddAssign, opStarDivAssign, opStarShlAssign, opStarCatAssign... no, that doesn't sound right.

Well you also can't do a[0] += n; a[0] /= n; a[0] <<= n; a[0] ~= n; for classes overloading opIndex() and opIndexAssign(). No, I just see two big annoyances with opStar (three if you count the fact that it's not called opDereference) (1) You're forced to write (*a).b, and there's no convenient shortcut like a->b (2) For a smart pointer to a struct, should *p return the entire struct, or a pointer to it? If the latter, then the following two lines would be completely equivalent: (**p).member; (*p).member; which is slightly counterintuitive. And that's ignoring complexities like what happens if a smart pointer points to a different kind of smart pointer which points to something else. In C++, the arrow operator is applied recursively until the compiler finds a class that doesn't overload it, which is exactly what you want. That said, C++ provides both operator*() and operator->(), and they can be overloaded independently, so if you're creating a smart pointer, you have to overload operator->() to return a pointer (because it's recursive), and then you have to overload operator*() to return a reference (because it isn't). So I guess I'm saying that (1) The expression a.b should interpretted as follows. If a has a member variable called b, then the expression is a reference to that; if a has a member function opStar(), and the return type has a member variable called b, then the expression is a reference to that; if both are simultaneously true then it is an compile error. (2) implicit in the above - operator overloads should be allowed to return references, so they can be lvalues. Then we can get rid of all the op*Assigns.
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Janice Caron <caron800 googlemail.com> wrote:
 On 11/10/07, Michel Fortin <michel.fortin michelf.com> wrote:
 And then opStarAddAssign, opStarDivAssign, opStarShlAssign,
 opStarCatAssign...   no, that doesn't sound right.

Well you also can't do a[0] += n; a[0] /= n; a[0] <<= n; a[0] ~= n; for classes overloading opIndex() and opIndexAssign().

You're right. It's a different beast. You'd /want/ to be able to do *p++ = *q++; Though of course you can already do: a[i++] = b[j++]; I always thought smart arrays were "the D way", but now I'm just confused.
Nov 10 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 So, we can write p.m for real pointers, but we have to write (*p).m
 for classes that implement opStar()? That seems inconsistent.

Not really. You cannot overload references in C++, either.

Nice looking herring there. I like the cool red color. Anyway, back on topic... (*p).m for iterators, versus p.m for pointers. That seems inconsistent. The way I see it is, (as others have pointed out), if iterators are to be of any use as "smart pointers", then they need to act like pointers. If they don't , then you might just as well use a member function like .val or .deref. If you want to force people to write (*p).m for real pointers, so that generic programming can accept either, then you're probably going to have to deprecate p.m for pointers.
 What's opStar for, if not to implement iterators?

For iterators.

Interesting. But I still don't understand how opStar() would help us do: *p++ = *q++;
 Should a class be used as an iterator? I can't see how
 that would make sense anyway.

Everything written above works just as well if you substitute "struct" for "class".
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Walter Bright Wrote:
 For iterators. Should a class be used as an iterator? I can't see how
 that would make sense anyway.

Are we to infer from that that iterators (and possibly smart pointers) might be a new kind of first class entity or did you just mean structs.

I think you're reading too much into that. Walter only said that classes as iterators doesn't make much sense. And he's right. Consider. Iterator p = collection.begin(); Iterator q = p; ++p; You wouldn't expect q to be incremented in the above code! Therefore, iterators must be passed by value. Therefore, they should be structs. But that doesn't change any of the reasoning about dereferencing and the dot operator.
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Michel Fortin <michel.fortin michelf.com> wrote:
 On 2007-11-11 00:42:07 -0500, Walter Bright <newshound1 digitalmars.com> said:

 What's opStar for, if not to implement iterators?

For iterators.

I've never like much C++ pointer-like iterators, and I don't think working with iterators should feel like we're manipulating pointers.

Are you aware of any primitive type other than a pointer which takes a unary asterisk operator, for any purpose whatsoever?
Nov 11 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because raw pointers don't have a .value property

It's your language, so you could give raw pointers whatever properties you wanted. They already have .sizeof and .init and .mangleof. If you wanted to, you could give pointers a .value property. Just saying "they don't have that property" is a silly argument for someone with your godlike powers over the language.
Nov 11 2007
parent "Kris" <foo bar.com> writes:
"Janice Caron" <caron800 googlemail.com> wrote in message 
news:mailman.39.1194815875.2338.digitalmars-d puremagic.com...
 On 11/11/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Because raw pointers don't have a .value property

It's your language, so you could give raw pointers whatever properties you wanted. They already have .sizeof and .init and .mangleof. If you wanted to, you could give pointers a .value property. Just saying "they don't have that property" is a silly argument for someone with your godlike powers over the language.

There's such a thing as a balanced and practical perspective. Just because Walter *could* add all kinds of things to the language doesn't mean that he *should* do so. Thus, I'd say perhaps you're the one with the silly argument :p
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 First, let's call it opDeref

Good plan!
 1) In D, the magic pointer dereferencing will only dereference one
 level.

If possible, I'd like to go the other way, and have both opStar AND implicit dereferencing * for pointers recurse as many times as required. So, if p were a pointer to a pointer to a struct then p.member would be equivalent to (*p).member, which would in turn be equivalent to (**p).member. Likewise, opDeref could be repeatedly applied until you reach a type which has no opDeref and isn't a pointer. There is some justification for this. In C++, that's operator-> is applied recursively until you read a type which has operator-> and isn't a pointer. It makes perfect sense in D not only because we use . instead of ->, but because otherwise, what would you have opStar return? We can't return references, remember. So should opDeref() return by value an entire struct, if the struct is large? I think it just needs to return a pointer, and then rely on the language to do the additional dereference.
 2) Make a special property that if used disables the opDeref.  Maybe
 even x.this.member could be used for that.  Actually, I think because of
 1) that should work automatically if x is a struct.

Ooh, I like that. So x.this.member never calls opDeref. Simple. Elegant. Perfect!
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/11/07, Janice Caron <caron800 googlemail.com> wrote:

Ooh that was full of typos! Not enough coffee! Ahem.

In C++, operator-> is applied recursively until you read a type which
has no operator-> and isn't a pointer.
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 2:17 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 There are other ways to do smart pointers. I happen to strongly dislike
 the C++ way of doing it.

Great! So do I! Which leads me to wonder why you gave us opStar(), which seems to me to be /exactly/ the C++ way (except without the convenience of ->)
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 2:19 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 Adding new properties is a pretty disruptive thing to do, especially
 common names.

I agree. If you were going to introduce a new property, I'd be the /first/ to argue that it shouldn't be called something as common as "value". There are plenty of less common alternatives. But that said, it wouldn't be my first choice. My first choice would be to have the dot operator automatically call opDeref as many times as necessary, so that iterators behave like pointers. The reason for all this fuss is that the status quo is not perfect. Suppose I want to implement an iterator which iterates over very large structs. Like this: struct VeryLargeStruct { int m; /* Lots of other stuff */ } struct Iterator { VeryLargeStruct opStar() { /*...*/ } } Now, to read m through an iterator, I could do Iterator p; int x = (*p).m; Ignoring for a moment that fact that most of us don't like the syntax, this will do the job. BUT, in order to do it, an /entire copy/ of a VeryLargeStruct has been copied onto the stack, just so that I can then copy one integer, This is what happens when you copy by value. To make matters worse: (*p).m = 10; Seems to me that, if this compiles at all, opStar will return a temporary copy of a VeryLargeStruct, and the element m /of that copy/ will be assigned. Again, this is what happens when you return by value. The obvious solution then is to return by reference... struct Iterator { ref VeryLargeStruct opStar() { /*...*/ } } But - oh wait! We can't return by reference. What a shame! Well - we could do the next best thing and return by pointer... struct Iterator { VeryLargeStruct * opStar() { /*...*/ } } That works. But now the syntax for dereferencing the iterator has changed. Now we have to write int x = (**p).m; (**p).m = 10; This is the reason I suggest that opStar (...please let it be opDeref...) be recursive, and that even one star be unnecessary. Mechanisms have been described on this thread which, so far as I can see, would work. The syntax we would end up with would be int x = p.m; p.m = 10; And that's gotta be a good thing, right?
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 12:40 AM, Bill Baxter <dnewsgroup billbaxter.com> wrote:

 Also I noticed that if smart member access is going to be the default
 then it seems necessary to make clear it doesn't apply to operators.
 That is, ++p may be equivalent to p.opPreIncr generally, but that
 doesn't mean that ++p will call p.opDeref.opPreIncr.  For that you'll
 still need the explicit dereference: ++(*p).

 Without that rule iterators will look pretty ugly.

You're right. Operators need to be an exception to the general rule. Well spotted. It's not really a exception though, because it would be /conforming/ to a higher principle - to the principle that: "iterators should behave like pointers". It's a principle which will lead to code that is not confusing.
Nov 11 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 2:19 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 Adding new properties is a pretty disruptive thing to do, especially
 common names.

I agree. If you were going to introduce a new property, I'd be the /first/ to argue that it shouldn't be called something as common as "value". There are plenty of less common alternatives. But that said, it wouldn't be my first choice. My first choice would be to have the dot operator automatically call opDeref as many times as necessary, so that iterators behave like pointers. The reason for all this fuss is that the status quo is not perfect. Suppose I want to implement an iterator which iterates over very large structs. Like this: struct VeryLargeStruct { int m; /* Lots of other stuff */ } struct Iterator { VeryLargeStruct opStar() { /*...*/ } } Now, to read m through an iterator, I could do Iterator p; int x = (*p).m; Ignoring for a moment that fact that most of us don't like the syntax, this will do the job. BUT, in order to do it, an /entire copy/ of a VeryLargeStruct has been copied onto the stack, just so that I can then copy one integer, This is what happens when you copy by value. To make matters worse: (*p).m = 10; Seems to me that, if this compiles at all, opStar will return a temporary copy of a VeryLargeStruct, and the element m /of that copy/ will be assigned. Again, this is what happens when you return by value. The obvious solution then is to return by reference... struct Iterator { ref VeryLargeStruct opStar() { /*...*/ } } But - oh wait! We can't return by reference. What a shame! Well - we could do the next best thing and return by pointer... struct Iterator { VeryLargeStruct * opStar() { /*...*/ } } That works. But now the syntax for dereferencing the iterator has changed. Now we have to write int x = (**p).m; (**p).m = 10; This is the reason I suggest that opStar (...please let it be opDeref...) be recursive, and that even one star be unnecessary. Mechanisms have been described on this thread which, so far as I can see, would work. The syntax we would end up with would be int x = p.m; p.m = 10; And that's gotta be a good thing, right?
Nov 11 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Janice,

 On Nov 12, 2007 2:19 AM, Walter Bright <newshound1 digitalmars.com>
 wrote:
 
 Adding new properties is a pretty disruptive thing to do, especially
 common names.
 

/first/ to argue that it shouldn't be called something as common as "value". There are plenty of less common alternatives. But that said, it wouldn't be my first choice. My first choice would be to have the dot operator automatically call opDeref as many times as necessary, so that iterators behave like pointers.

struct S1 { int a; } struct S2 { int c; S1 opDeref(); } struct S3 { int b; S2 opDeref(); } struct S4 { int d; S3 opDeref(); } S4 s; s.a; //-> a.opDeref.opDeref.opDeref.a s.b; //-> a.opDeref.b s.c; //-> a.opDeref.opDeref.c s.d; //-> a.d this causes some odd "tiny changes make huge differences" issues along with another hijacking case. I'm just saying these need to be considered, not that they are killers to the idea.
Nov 12 2007
parent BCS <BCS pathlink.com> writes:
Janice Caron wrote:
 On 11/12/07, BCS <ao pathlink.com> wrote:
 
s.a; //-> a.opDeref.opDeref.opDeref.a

You mean s.opDeref.opDeref.opDeref.a Yes

:b
 
s.b; //-> a.opDeref.b

If we go with Bill Baxter's idea, that would be a compile error, since a.opDeref.opDeref.opDeref has no member called b. To get at b, you'd have to do: s.this.opDeref.this.b

I wasn't interested in the expanded form. My issue/question/etc was with regards to the issue of "IDENT.IDENT can result in any number of derefs depending on the type". I don't see any clean way around this. I'll admit I haven't followed the suggestions closely so I could have missed something.
Nov 12 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/12/07, BCS <ao pathlink.com> wrote:
 s.a; //-> a.opDeref.opDeref.opDeref.a

You mean s.opDeref.opDeref.opDeref.a Yes
 s.b; //-> a.opDeref.b

If we go with Bill Baxter's idea, that would be a compile error, since a.opDeref.opDeref.opDeref has no member called b. To get at b, you'd have to do: s.this.opDeref.this.b ...or something! Obviously the syntax is still under discussion. But go back and look at Bill's excellent post in which he makes clear that going through opDeref should be done always (if it exists), unless explicitly prevented by some means.
 s.c; //-> a.opDeref.opDeref.c
 s.d; //-> a.d

Likewise.
 this causes some odd "tiny changes make huge differences" issues along with
 another hijacking case.

Yeah, I think Bill covered that. Of course the syntax isn't nailed down yet.
 I'm just saying these need to be considered, not that they are killers to
 the idea.

They do indeed. I agree.
Nov 12 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 12:40 AM, Bill Baxter <dnewsgroup billbaxter.com> wrote:

 Also I noticed that if smart member access is going to be the default
 then it seems necessary to make clear it doesn't apply to operators.
 That is, ++p may be equivalent to p.opPreIncr generally, but that
 doesn't mean that ++p will call p.opDeref.opPreIncr.  For that you'll
 still need the explicit dereference: ++(*p).

 Without that rule iterators will look pretty ugly.

You're right. Operators need to be an exception to the general rule. Well spotted. It's not really a exception though, because it would be /conforming/ to a higher principle - to the principle that: "iterators should behave like pointers". It's a principle which will lead to code that is not confusing.
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 12, 2007 8:50 AM, Walter Bright <newshound1 digitalmars.com> wrote:
 1) copy
 2) increment
 3) compare for equality
 4) get the value

You missed 5) assignment (And also decrement, but I guess increment and decrement are both just special cases of adding or subtracting an integer) You also haven't addressed the point that since no function, opStar included, can return a reference to a struct, then we are forced to return a pointer to a struct, so now your iterator behaves not like a pointer, but a pointer to a pointer.
Nov 12 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/12/07, Walter Bright <newshound1 digitalmars.com> wrote:
 You also haven't addressed the point that since no function, opStar
 included, can return a reference to a struct, then we are forced to
 return a pointer to a struct, so now your iterator behaves not like a
 pointer, but a pointer to a pointer.

Iterators should be very small value types. I don't see why one would be returning a reference to an iterator.

OK, I'm not being understood. Assume you have this defined... struct VeryLargeStruct { /*...*/ } Now have a collection of those things. class CollectionOfVeryLargeStructs { /*...*/ } Then you get an iterator CollectionOfVeryLargeStructs c; IteratorToVeryLargeStruct p = c.begin(); p itself is very small, being an instance of IteratorToVeryLargeStruct. My question relates, not to p, but to p.opStar(). What type should p.opStar() return? Should it return (by value) a VeryLargeStruct? Or should it return a VeryLargeStruct* ? (Of course, in C++, we'd just have operator*() return a VeryLargeStuct&. Problem solved). If the latter, then p is behaving like a pointer-to-a-pointer, not a pointer, and now you'd have to (**p), not (*p), to reach an actual VeryLargeStruct, and you'd have to write (**p).m to access the members thereof. You see?
Nov 12 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/12/07, Walter Bright <newshound1 digitalmars.com> wrote:
YOU:
 It's for iterators.
 To do an iterator, you need ...
 1) copy

 ... already covered by operator overloading

ME:
 You missed
 5) assignment

YOU:
 I think (1) covers that.

My apologies for not getting this. How does opStar give us assignment? As in *p = n; ...unless opStar can return an lvalue? Is that what you're saying? Are we finally getting ref return types?
Nov 12 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On Nov 13, 2007 6:17 AM, David B. Held <dheld codelogicconsulting.com> wrote:
 int size[3] = { length, width, height };
 std::sort(size + 0, size + 3);

 I do lots of little stuff like this, which is very convenient.

In D: size.sort; Pointers not needed.
Nov 12 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/13/07, Bruno Medeiros <brunodomedeiros+spam com.gmail> wrote:
 How would the pointer idiom have any advantage in terms of performance??
 (versus say, structs with hasNext() and next() member functions)

To add to that, how would the pointer idiom have any advantage in terms of performance over foreach? T[] array; foreach(element;array) { /*...*/ }
Nov 13 2007