www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - forward range properties with alias this - how?

reply "Ivan Kazmenko" <gassa mail.ru> writes:
Hello,

I wrap an array into a struct.  Then I use alias this to expose 
the array functionality.  Sadly, range properties of the array 
are not forwarded, and so I can't use the struct as an array with 
functions from std.algorithm and std.range.

-----
import std.range, std.stdio;
struct S {
	int[] contents;
	alias contents this;
}
void main() {
	S s;
	writeln(hasSlicing!(S)); // false
}
-----

I would like to be able to do that, however.

1. Why exactly hasSlicing (et al.) does not check the alias 
this-ed array when it checks the struct?

2. What should I do?

The solution I can think of is to insert the 3-6 range functions 
which forward the functionality to the underlying array, perhaps 
as a mixin.

Ivan Kazmenko.
Jul 28 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 28 July 2015 at 21:25:23 UTC, Ivan Kazmenko wrote:
 Hello,

 I wrap an array into a struct.  Then I use alias this to expose 
 the array functionality.  Sadly, range properties of the array 
 are not forwarded, and so I can't use the struct as an array 
 with functions from std.algorithm and std.range.

 -----
 import std.range, std.stdio;
 struct S {
 	int[] contents;
 	alias contents this;
 }
 void main() {
 	S s;
 	writeln(hasSlicing!(S)); // false
 }
 -----

 I would like to be able to do that, however.

 1. Why exactly hasSlicing (et al.) does not check the alias 
 this-ed array when it checks the struct?

 2. What should I do?

 The solution I can think of is to insert the 3-6 range 
 functions which forward the functionality to the underlying 
 array, perhaps as a mixin.

 Ivan Kazmenko.
`hasSlicing` explicitly checks whether the result of the slice operator has the same type as the original: https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L1499-L1500 If you remove the `static assert()` and change the next line to use `auto`, and do the same in the other two places in this templates, it will work. I don't know whether this is intentional. I'd say we should allow a sliceable range to have slices of a different type. EDIT: The documentation even says that it's intentional, but gives no justification.
Jul 29 2015
parent reply "Ivan Kazmenko" <gassa mail.ru> writes:
On Wednesday, 29 July 2015 at 12:25:14 UTC, Marc Schütz wrote:
 On Tuesday, 28 July 2015 at 21:25:23 UTC, Ivan Kazmenko wrote:
 Hello,

 I wrap an array into a struct.  Then I use alias this to 
 expose the array functionality.  Sadly, range properties of 
 the array are not forwarded, and so I can't use the struct as 
 an array with functions from std.algorithm and std.range.

 -----
 import std.range, std.stdio;
 struct S {
 	int[] contents;
 	alias contents this;
 }
 void main() {
 	S s;
 	writeln(hasSlicing!(S)); // false
 }
 -----

 I would like to be able to do that, however.

 1. Why exactly hasSlicing (et al.) does not check the alias 
 this-ed array when it checks the struct?

 2. What should I do?

 The solution I can think of is to insert the 3-6 range 
 functions which forward the functionality to the underlying 
 array, perhaps as a mixin.

 Ivan Kazmenko.
`hasSlicing` explicitly checks whether the result of the slice operator has the same type as the original: https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L1499-L1500 If you remove the `static assert()` and change the next line to use `auto`, and do the same in the other two places in this templates, it will work.
Thank you, the matter got clearer after reading the right piece of code and your explanation. By the way, the documentation around these source lines always repeats a slightly outdated version of the unittests. Shouldn't it be brought in sync, perhaps by using the modern DRY way: /// unittest {...} Or will that necessarily precede the unittest with "Example:"? At any rate, I doubt the ~20 lines of introspection code - which sometimes gets outdated - should appear at all in hasSlicing et al. documentation. If a developer encounters problems with hasSlicing and needs the source, a link to the up-to-date source itself may be enough.
 I don't know whether this is intentional. I'd say we should 
 allow a sliceable range to have slices of a different type.

 EDIT:
 The documentation even says that it's intentional, but gives no 
 justification.
I don't know why the type should be the same, but that may well be needed. Anyway, after more digging, I found out I only need to implement save() to satisfy isRandomAccessRange, which makes sense when I think of it: the save() for arrays returns an array and not my struct. And opSlice(...) to satisfy hasSlicing, which also makes sense if we accept that the slice needs to be the same type: a generic opSlice is not possible since operator overloads must be member functions, and even if it were, it would not know how to construct an object of our specific type in the general case. So, that's some boilerplate, but its appearance seems justified. Here's the working code I got: ----- import std.algorithm, std.random, std.range, std.stdio; struct S { int[] contents; alias contents this; property auto save() {return S(contents.dup);} auto opSlice(size_t lo, size_t hi) {return S(contents[lo..hi]);} } void main() { S s; s = [4, 3, 2, 1]; writeln(s[1..3]); // [3, 2] writeln(isInputRange!(S)); // true writeln(isForwardRange!(S)); // true writeln(isBidirectionalRange!(S)); // true writeln(isRandomAccessRange!(S)); // true writeln(hasSlicing!(S)); // true auto t = s; sort(s); writeln(s); // [1, 2, 3, 4] randomShuffle(s); writeln(s); // random permutation writeln(t); // same as above } ----- Now, if I remove the custom opSlice and alter the checks in hasSlicing as you suggested, I get the error: ----- sorting.d(1160): Error: quickSortImpl (S r, uint depth) is not callable using argument types (int[], uint) ----- Which means quickSortImpl (S r, uint depth) can't instantiate and call quickSortImpl (int[] r, uint depth) for recursively sorting its slices. That is understandable.
Jul 29 2015
parent "Ivan Kazmenko" <gassa mail.ru> writes:
On Wednesday, 29 July 2015 at 23:54:29 UTC, Ivan Kazmenko wrote:
 On Wednesday, 29 July 2015 at 12:25:14 UTC, Marc Schütz wrote:
 On Tuesday, 28 July 2015 at 21:25:23 UTC, Ivan Kazmenko wrote:
 ...
Perhaps I still don't implement save() correctly. The line property auto save() {return S(contents.dup);} was meant to be just: property auto save() {return S(contents);} But with either of them, I get an error when trying a function using save. ----- import std.algorithm, std.range, std.stdio; struct S { int[] contents; alias contents this; property auto save() {return S(contents);} auto opSlice(size_t lo, size_t hi) {return S(contents[lo..hi]);} } void main() { S s; s = [4, 3, 2, 1]; nextPermutation(s); } ----- The error is: ----- sorting.d(2460): Warning: use std.algorithm.reverse instead of .reverse property sorting.d(2460): Error: function expected before (), not _adReverse(range.contents, 4u) of type int[] ----- So, is something wrong with my save()? Anyway, I reckon nextPermutation itself is wrong and should use reverse(range); instead of range.reverse(); as it does ten lines later: https://github.com/D-Programming-Language/phobos/blob/30e4ff1717d6d3eb82d2cb0e00a3c07af4263a7b/std/algorithm/sorting.d#L2468-L2478 If anybody can confirm that, I can file an issue and a patch.
Jul 29 2015