Thread overview | |||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 07, 2020 Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
If I have an input range with element type `int[3]`, how do I easily turn it into a range of `int` so I can map it? If it were an int[3][] I could simply cast it to an int[] before mapping, but I don't want to eagerly turn it into an array. I thought of doing this: ``` range.map!(x => x[]).joiner.map!(x => x*2); ``` But it gives: Error: returning x[] escapes a reference to parameter x, perhaps annotate with return I tried doing: ``` map!((return x) => x[]) // same error map!((return ref x) => x[]) // does not match, map.front is not an lvalue ``` I can easily work around it with some more code, but I wonder if someone knows an easy solution. |
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On 2/7/20 3:13 PM, Dennis wrote: > If I have an input range with element type `int[3]`, how do I easily turn it into a range of `int` so I can map it? > If it were an int[3][] I could simply cast it to an int[] before mapping, but I don't want to eagerly turn it into an array. > I thought of doing this: > ``` > range.map!(x => x[]).joiner.map!(x => x*2); > ``` > > But it gives: > Error: returning x[] escapes a reference to parameter x, perhaps annotate with return This is correct. Consider that you did not mark x as ref. So what x actually is is a function local. If you returned x[], you just returned a reference to a local, which is about to go out of scope. > I tried doing: > ``` > map!((return x) => x[]) // same error > map!((return ref x) => x[]) // does not match, map.front is not an lvalue > ``` > > I can easily work around it with some more code, but I wonder if someone knows an easy solution. This means your actual input range is not an array (map should forward the lvalueness of it). This means you can't just slice, as you will again return references to the local stack frame. The only solution I can provide is to wrap the static array into a range (maybe something like this exists in Phobos?): struct SARange(T, size_t N) { T[N] storage; size_t elem; auto front() { return storage[elem]; } void popFront() { ++elem; } bool empty() { return elem >= N; } } auto asRange(T : E[N], E, size_t N)(T val) { return SARange!(E, N)(val); } range.map!(x => x.asRange).joiner.map!(x => x*2); -Steve |
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On Friday, 7 February 2020 at 20:13:57 UTC, Dennis wrote:
> If I have an input range with element type `int[3]`, how do I easily turn it into a range of `int` so I can map it?
> If it were an int[3][] I could simply cast it to an int[] before mapping, but I don't want to eagerly turn it into an array.
> I thought of doing this:
> ```
> range.map!(x => x[]).joiner.map!(x => x*2);
> ```
>
> But it gives:
> Error: returning x[] escapes a reference to parameter x, perhaps annotate with return
>
> I tried doing:
> ```
> map!((return x) => x[]) // same error
> map!((return ref x) => x[]) // does not match, map.front is not an lvalue
> ```
>
> I can easily work around it with some more code, but I wonder if someone knows an easy solution.
Depending on how your range is structured, it might be possible to just mark front as returning by ref to make this work.
|
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to nullptr | On Friday, 7 February 2020 at 20:55:14 UTC, nullptr wrote: > Depending on how your range is structured, it might be possible to just mark front as returning by ref to make this work. That's a good one. I can't make front() return by ref, but I can make front a member variable of the range struct. Only problem: @safe function ... cannot call @system function std.algorithm.iteration.joiner!(...).joiner I don't know why. I don't have time to delve into this at the moment but if anyone wants to try, here's a minimal testcase: ``` import std; struct S { int[3] front = [10, 20, 30]; bool empty = false; void popFront() {empty = true;} } void main() @safe { S.init.map!((return ref x) => x[]).joiner.writeln; } ``` flags: -dip1000 -dip25 |
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 7 February 2020 at 20:31:47 UTC, Steven Schveighoffer wrote:
> The only solution I can provide is to wrap the static array into a range (maybe something like this exists in Phobos?):
Thanks. I was hoping something like that existed in Phobos, but I can't find anything.
|
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On 2/7/20 4:17 PM, Dennis wrote:
> On Friday, 7 February 2020 at 20:55:14 UTC, nullptr wrote:
>> Depending on how your range is structured, it might be possible to just mark front as returning by ref to make this work.
>
> That's a good one. I can't make front() return by ref, but I can make front a member variable of the range struct. Only problem:
> @safe function ... cannot call @system function std.algorithm.iteration.joiner!(...).joiner
>
> I don't know why. I don't have time to delve into this at the moment but if anyone wants to try, here's a minimal testcase:
>
> ```
> import std;
>
> struct S {
> int[3] front = [10, 20, 30];
> bool empty = false;
> void popFront() {empty = true;}
> }
>
> void main() @safe {
> S.init.map!((return ref x) => x[]).joiner.writeln;
> }
> ```
> flags: -dip1000 -dip25
>
S.popFront is not @safe, and S is not a template. So no inferrence.
But I did that, and it still doesn't compile. Not sure why not. Another fun dip1000 problem...
-Steve
|
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 7 February 2020 at 21:40:36 UTC, Steven Schveighoffer wrote:
> S.popFront is not @safe, and S is not a template. So no inferrence.
Oops, minimized a bit too much. Corrected test case:
```
import std;
struct S {
@safe:
int[3] front = [10, 20, 30];
bool empty = false;
void popFront() {empty = true;}
}
void main() @safe {
S.init.map!((return ref x) => x[]).joiner.writeln;
}
```
It indeed still errors with -dip1000, but without -dip1000 it compiles now interestingly.
|
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dennis | On Friday, 7 February 2020 at 22:55:29 UTC, Dennis wrote: > Oops, minimized a bit too much. Corrected test case: > ``` > import std; > > struct S { > @safe: > int[3] front = [10, 20, 30]; > bool empty = false; > void popFront() {empty = true;} > } > > void main() @safe { > S.init.map!((return ref x) => x[]).joiner.writeln; > } > ``` > It indeed still errors with -dip1000, but without -dip1000 it compiles now interestingly. ``` import std; struct SomeRange { int[3] val; enum empty = false; auto popFront() @safe {} ref auto front() @safe { return val; } } void main() @safe { SomeRange().take(10).map!((return ref x) => x[]).joiner.writeln; } ``` I don't know how applicable this is to your use case, but this code will compile and run under -dip1000. |
February 08, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to nullptr | On 08.02.20 00:10, nullptr wrote:
> ```
> import std;
>
> struct SomeRange
> {
> int[3] val;
>
> enum empty = false;
>
> auto popFront() @safe {}
>
> ref auto front() @safe
> {
> return val;
> }
> }
>
> void main() @safe
> {
> SomeRange().take(10).map!((return ref x) => x[]).joiner.writeln;
> }
> ```
>
> I don't know how applicable this is to your use case, but this code will compile and run under -dip1000.
That shouldn't compile. You have found a hole in DIP 1000.
----
struct SomeRange
{
int[3] val = [10, 20, 30];
ref auto front() @safe { return val; }
}
int[] f() @safe
{
SomeRange sr;
// return sr.val[]; /* error, as expected */
return sr.front[]; /* no error, but escapes reference to local */
}
void main() @safe
{
auto x = f();
import std.stdio;
writeln(x); /* Prints garbage. */
}
----
I'm too lazy right now to check if it's already in Bugzilla.
|
February 07, 2020 Re: Flatten a range of static arrays | ||||
---|---|---|---|---|
| ||||
Posted in reply to nullptr | On 2/7/20 6:10 PM, nullptr wrote:
> ```
> import std;
>
> struct SomeRange
> {
> int[3] val;
>
> enum empty = false;
>
> auto popFront() @safe {}
>
> ref auto front() @safe
> {
> return val;
> }
> }
>
> void main() @safe
> {
> SomeRange().take(10).map!((return ref x) => x[]).joiner.writeln;
> }
> ```
>
> I don't know how applicable this is to your use case, but this code will compile and run under -dip1000.
I tried the AST button. for the front function I got:
return pure nothrow @nogc ref @safe int[3] front() return
Note the duplicate return attributes. When I type that in, it doesn't compile, the return at the front is invalid.
When I remove that return, I get a safety error. So something is weird about how the attributes are inferred. Either there is some magic attribute not being printed, or an attribute that can't be specified manually, or it's a bug.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation