Bug 3474 – PATCH: Implement opDollar for struct and class indexing operations

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
Other
OS
All
Creation time
2009-11-05T07:33:00Z
Last change time
2015-06-09T01:27:02Z
Keywords
patch
Assigned to
nobody
Creator
clugdbug

Attachments

IDFilenameSummaryContent-TypeSize
491patchopDollar.patchPatch against DMD2 svn 240.text/plain5886

Comments

Comment #0 by clugdbug — 2009-11-05T07:33:37Z
Created attachment 491 Patch against DMD2 svn 240. The attached patch allows usage of $ inside opIndex and opIndexAssign in structs and classes. Any usage of $ becomes a call to an opDollar function. If multi-dimensional indexing is possible, the $ symbol instantiates a template called opDollar(int dim)(). x[ $-3, $-5, $-6] becomes x.opIndex(x.opDollar!(0)() - 3, x.opDollar!(1)() - 5, x.opDollar!(2)() -6); Note that since opDollar is a template, it may return different types for different indices... As a convenience, if a class or struct ONLY supports single-dimension indexing, a non-templated opDollar() can be used instead. x[ $-5 ] can become x.opIndex( x.opDollar() - 5 ); (If a opDollar!(int n)() is available, it will be used instead. There's no ambiguity: the compiler won't let you define both template and function opDollar, since they both have no arguments). Some implementation notes: (1) $ is evaluated lazily for each dimension. x[4, $+3*$ - foo($), 6] will only make one call to opDollar. (2) BUG 3326: "$ in delegate literal causes Access Violation" does NOT apply to this code. You can do all kinds of nasty stuff with $ and it seems to work. (3) It is possible to nest multi-dimensional indexing. x[$-2, y[$-6, $-9], $-2] works. (4) I have NOT implemented $ inside opSlice(), opSliceAssign(). It could be done, but I believe those language features need work. They don't permit multi-dimensional slicing. I think they should be removed, and the functionality folded into opIndex. (5) How it works: x[ $-3, 5, $-6]= "abc" actually becomes: x.opIndexAssign("abc", (auto __dollar = x.opDollar!(0)();, __dollar - 3), 5, (auto __dollar = x.opDollar!(2)(); _dollar - 6)).
Comment #1 by andrei — 2009-11-05T08:13:37Z
Great! Any chance we can use "length" instead of "opDollar" throughout? It would instantly match what arrays are currently doing and it would be a closer description of the semantics of the operation.
Comment #2 by clugdbug — 2009-11-05T08:50:28Z
(In reply to comment #1) > Great! > > Any chance we can use "length" instead of "opDollar" throughout? It would > instantly match what arrays are currently doing and it would be a closer > description of the semantics of the operation. Yes, the name doesn't make any difference. Although length() looks natural, I think length!(1)() looks a bit clumsy? The one-dimensional case is a degenerate form of the multi-dimensional case. Moreover, I suspect the template is not the interface you want in most cases: you often want to iterate over the dimensions. Probably in most cases, it'd be defined like: int opDollar(int n)() { return dim[n]; } Also there may be problems if you want to be able to use .length to change the dimensions. BTW you can use aliases. This works: struct Foo { int x; int length() { return x; } alias length opDollar; int opIndex(int k) { return x*k; } } void main() { Foo f = Foo(7); int x = foo[$-5]; assert(x== 2*7); }
Comment #3 by andrei — 2009-11-05T09:18:44Z
(In reply to comment #2) > (In reply to comment #1) > > Great! > > > > Any chance we can use "length" instead of "opDollar" throughout? It would > > instantly match what arrays are currently doing and it would be a closer > > description of the semantics of the operation. > > Yes, the name doesn't make any difference. > Although length() looks natural, I think length!(1)() looks a bit clumsy? > The one-dimensional case is a degenerate form of the multi-dimensional case. > Moreover, I suspect the template is not the interface you want in most cases: > you often want to iterate over the dimensions. Probably in most cases, it'd be > defined like: > > int opDollar(int n)() { return dim[n]; } > > Also there may be problems if you want to be able to use .length to change the > dimensions. > > BTW you can use aliases. This works: > > struct Foo > { > int x; > int length() { return x; } > alias length opDollar; > int opIndex(int k) { return x*k; } > } > > void main() > { > Foo f = Foo(7); > int x = foo[$-5]; > assert(x== 2*7); > } I understand and have no further comments. Let's go with opDollar. (One suggestion if I may :o). In the 1-dimensional case, of a type defines no opDollar but does define length... you may want to rewrite $ to length. But probably that's too complicated a rule already.)
Comment #4 by smjg — 2009-11-05T15:09:23Z
(In reply to comment #1) > Great! > > Any chance we can use "length" instead of "opDollar" throughout? It would > instantly match what arrays are currently doing and it would be a closer > description of the semantics of the operation. I agree that "opDollar" is a bad choice of name, but so is "length". AIUI the point is for it to represent the end (or one past the end) of the array, rather than length. They correspond only in the case of 0-based arrays. This has come up before: http://www.digitalmars.com/d/archives/digitalmars/D/announce/Re_opDollar_12939.html
Comment #5 by dfj1esp02 — 2009-11-06T02:24:30Z
> I agree that "opDollar" is a bad choice of name, but so is "length". AIUI the > point is for it to represent the end (or one past the end) of the array, rather > than length. They correspond only in the case of 0-based arrays. It's elready defined semantics. $ means length. If you want different semantics, use different means to express it. Objects in container can be numbered from 1, numbering can have gaps, so $-1 can be meaningless, or entire collection can be unordered like hashtable, so the last element can be meaningless, or keytype can be of complex type, having nothing in common with numbers, you just use different means to work with it.
Comment #6 by smjg — 2009-11-06T06:43:47Z
(In reply to comment #5) >> I agree that "opDollar" is a bad choice of name, but so is >> "length". AIUI the point is for it to represent the end (or one >> past the end) of the array, rather than length. They correspond >> only in the case of 0-based arrays. > > It's elready defined semantics. $ means length. It's defined only on built-in linear arrays: http://www.digitalmars.com/d/2.0/arrays.html "Within the [ ] of a static or a dynamic array, the variable length is implicitly declared and set to the length of the array. The symbol $ can also be so used." Try it on AAs and see for yourself. (Sidenote: I thought the implicitly declared length was deprecated.) Moreover, FAIK, it may have been described this way only for simplicity, as Walter never expected to make it overloadable for custom types where "length" and "index one after the end" are distinct. > If you want different semantics, use different means to express it. > Objects in container can be numbered from 1, numbering can have > gaps, so $-1 can be meaningless, You're giving me exactly the already-known reasons $ ought to be defined along the lines of "end" rather than "length". It's probably how most D programmers think of it, because the main use for $ is to index relative to the end of an array. And so, if we start encouraging programmers to make $ mean the number of elements in a non-0-based array, sparse array or whatever, it'll get confusing. I certainly can't see any reason for keeping it as "length". And it would break 0% of existing code to change it now. As for "numbering can have gaps", there are at least two cases to consider: - indexes are spaced by a constant interval k, IWC [$-k] feels a natural way of referring to the final element analogous to [$-1] for standard arrays - indexes are (or may be) irregularly spaced, IWC what $ should mean, if anything, is harder to define > or entire collection can be unordered like hashtable, so the last > element can be meaningless, or keytype can be of complex type, > having nothing in common with numbers, you just use different means > to work with it. IWC you would probably not define $ at all. If there's any problem with that, I can't see it....
Comment #7 by adrian — 2010-08-08T06:19:55Z
Any solution of this? Currently (DMD 2.047) both length and opDollar give "undefined identifier __dollar".
Comment #8 by bearophile_hugs — 2011-07-03T12:57:09Z
Have you created a pull request for this, Don?
Comment #9 by k.hara.pg — 2011-10-09T06:49:42Z
Applied Don's patch into git master. https://github.com/9rnsr/dmd/branches/opDollar
Comment #10 by k.hara.pg — 2011-10-10T02:33:26Z
Comment #11 by bearophile_hugs — 2011-11-22T00:05:51Z
With the recent patch this doesn't compile, is this expected? struct Foo { int x; alias x opDollar; int opIndex(int k) { return x * k; } } void main() { Foo f = Foo(7); int x = f[$ - 5]; // Error: undefined identifier __dollar assert(x == 2 * 7); }
Comment #12 by clugdbug — 2011-11-22T00:30:59Z
(In reply to comment #11) > With the recent patch this doesn't compile, is this expected? > > > struct Foo { > int x; > alias x opDollar; > int opIndex(int k) { return x * k; } > } > void main() { > Foo f = Foo(7); > int x = f[$ - 5]; // Error: undefined identifier __dollar > assert(x == 2 * 7); > } Yes, it checks that opDollar is either a function or a template function.
Comment #13 by clugdbug — 2011-11-22T00:43:02Z
Poor error message though. All the overloaded operators have bad errors: struct S { void x() {} alias x opUnary; } void main() { S a; a = -a; } test.d(9): Error: a.x isn't a template struct S { int x; alias x opUnary; } void main() { S a; a = -a; } test.d(9): Error: 'a' is not of arithmetic type, it is a S
Comment #14 by bugzilla — 2011-11-22T02:56:58Z