www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to return a reference to structs?

reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
I have the following code example:

```d
struct A{}

A[5] a;
ulong[] idx = [1,3,4];

auto get()
{
     return idx.map!(_ => a[_]);
}

foreach(i; 0 .. a.length)
     write(&a[i], " ");
writeln;

foreach(ref b; get())
     write(&b, " ");
writeln;
```

How can I change `get` function so it returns the references to 
the content in `a`?

Is there a good documentation describing how to avoid copying of 
structs in the code?
Nov 04 2021
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 4 November 2021 at 11:26:30 UTC, Andrey Zherikov 
wrote:
 I have the following code example:

 ```d
 struct A{}

 A[5] a;
 ulong[] idx = [1,3,4];

 auto get()
 {
     return idx.map!(_ => a[_]);
 }

 foreach(i; 0 .. a.length)
     write(&a[i], " ");
 writeln;

 foreach(ref b; get())
     write(&b, " ");
 writeln;
 ```

 How can I change `get` function so it returns the references to 
 the content in `a`?
Have the lambda return by reference: ```d auto get() { return idx.map!(ref (i) => a[i]); } ```
Nov 04 2021
next sibling parent Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Thursday, 4 November 2021 at 13:03:54 UTC, Paul Backus wrote:
 Have the lambda return by reference:

 ```d
 auto get()
 {
     return idx.map!(ref (i) => a[i]);
 }
 ```
That works, thanks!
Nov 04 2021
prev sibling parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Thursday, 4 November 2021 at 13:03:54 UTC, Paul Backus wrote:
 On Thursday, 4 November 2021 at 11:26:30 UTC, Andrey Zherikov 
 wrote:
 I have the following code example:

 ```d
 struct A{}

 A[5] a;
 ulong[] idx = [1,3,4];

 auto get()
 {
     return idx.map!(_ => a[_]);
 }

 foreach(i; 0 .. a.length)
     write(&a[i], " ");
 writeln;

 foreach(ref b; get())
     write(&b, " ");
 writeln;
 ```

 How can I change `get` function so it returns the references 
 to the content in `a`?
Have the lambda return by reference: ```d auto get() { return idx.map!(ref (i) => a[i]); } ```
Making this example a bit complex: I want `get` to return additional data to the reference. How should I change the lambda then? ```d auto get() { return idx.map!(ref (i) => tuple(a[i], i)); // return ref and index for simplicity } ```
Nov 04 2021
parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 4 November 2021 at 18:21:06 UTC, Andrey Zherikov 
wrote:
 On Thursday, 4 November 2021 at 13:03:54 UTC, Paul Backus wrote:
 Have the lambda return by reference:

 ```d
 auto get()
 {
     return idx.map!(ref (i) => a[i]);
 }
 ```
Making this example a bit complex: I want `get` to return additional data to the reference. How should I change the lambda then? ```d auto get() { return idx.map!(ref (i) => tuple(a[i], i)); // return ref and index for simplicity } ```
Since there are no such thing as `ref` variables in D, you can't store a reference inside a tuple. So in this case, you will have to use a pointer: ```d auto get() { return idx.map!((i) => tuple(&a[i], i)); } ```
Nov 04 2021
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 4 November 2021 at 11:26:30 UTC, Andrey Zherikov 
wrote:
 I have the following code example:
[...]
 A[5] a;
 ulong[] idx = [1,3,4];
[...]
     return idx.map!(_ => a[_]);
How can one make the type of idx's elements portable (compile with -m32 to see what the problem is)? This seems to be an surprisingly nontrivial issue at least in C [1]. [1] <https://stackoverflow.com/questions/3174850/what-is-the-correct-type-for-array-indexes-in-c>
Nov 05 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/5/21 11:44 AM, kdevel wrote:
 On Thursday, 4 November 2021 at 11:26:30 UTC, Andrey Zherikov wrote:
 I have the following code example:
[...]
 A[5] a;
 ulong[] idx = [1,3,4];
[...]
     return idx.map!(_ => a[_]);
How can one make the type of idx's elements portable (compile with -m32 to see what the problem is)?
Indexes are typed as size_t, which is ulong for 64-bit and uint for 32-bit. If idx elements were indeed indexes, it should have been typed as size_t: import std.algorithm; import std.stdio; struct A{} A[5] a; size_t[] idx = [1,3,4]; // <-- Here auto get() { return idx.map!(_ => a[_]); } void main() { foreach(i; 0 .. a.length) write(&a[i], " "); writeln; foreach(ref b; get()) write(&b, " "); writeln; } Ali
Nov 05 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Friday, 5 November 2021 at 19:49:50 UTC, Ali Çehreli wrote:
 Indexes are typed as size_t, which is ulong for 64-bit and uint 
 for 32-bit. If idx elements were indeed indexes, it should have 
 been typed as size_t:
 
 size_t[] idx = [1,3,4];  // <-- Here
Ah, good point. Thanks!
Nov 05 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Friday, 5 November 2021 at 20:13:02 UTC, Andrey Zherikov wrote:
 On Friday, 5 November 2021 at 19:49:50 UTC, Ali Çehreli wrote:
 Indexes are typed as size_t, which is ulong for 64-bit and 
 uint for 32-bit. If idx elements were indeed indexes, it 
 should have been typed as size_t:
 
 size_t[] idx = [1,3,4];  // <-- Here
Ah, good point. Thanks!
Adding a bit of CTFE: ```d struct A {} struct B { A[] ar = [A.init]; size_t[] idx = [0]; A*[] get() { return idx.map!((idx) => &ar[idx]).array; // Error: couldn't find field `ar` of type `A[]` in `MapResult([0LU], null)` } } auto foo() { B b; return b.get(); } void main() { writeln(foo()); // This works pragma(msg, foo()); // This doesn't work } ``` How to make this work?
Nov 05 2021
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/5/21 5:43 PM, Andrey Zherikov wrote:

In case others want to work, here are the modules that need to be imported:

import std.algorithm;
import std.range;
import std.stdio;

 struct A {}
 struct B
 {
      A[] ar = [A.init];
      size_t[] idx = [0];
I don't know why but those initial values are the reason for the problem. It works without them.
      A*[] get()
      {
          return idx.map!((idx) => &ar[idx]).array;  // Error: couldn't
 find field `ar` of type `A[]` in `MapResult([0LU], null)`
      }
 }

 auto foo()
 {
      B b;
      return b.get();
 }

 void main()
 {
      writeln(foo());       // This works
      pragma(msg, foo());   // This doesn't work
 }
Ali
Nov 05 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 6 November 2021 at 04:28:05 UTC, Ali Çehreli wrote:
 On 11/5/21 5:43 PM, Andrey Zherikov wrote:

 In case others want to work, here are the modules that need to 
 be imported:

 import std.algorithm;
 import std.range;
 import std.stdio;

 struct A {}
 struct B
 {
      A[] ar = [A.init];
      size_t[] idx = [0];
I don't know why but those initial values are the reason for the problem. It works without them.
`get()` will return nothing in this case. But I can make them non empty in another way: ```d import std; struct A {} struct B { A[] ar; size_t[] idx; A*[] get() { return idx.map!((idx) => &ar[idx]).array; } } auto foo() { B b; b.ar ~= A.init; b.idx ~= 0; return b.get(); } void main() { writeln(foo()); pragma(msg, foo()); } ``` Some one can complain that `foo()` returns pointers that are not available in CTFE but remember that the real code is more complex and `foo()` cam be return just length (`return b.get().length;`) having the same result. Basically I have a collection of data (`A[] ar`), different addressing through the indexes (multiple `size_t[] idx`) and I want to manipulate these objects through indexes. If there is another way to achieve the same in CFTE, I'd like to hear.
Nov 06 2021
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/6/21 5:05 AM, Andrey Zherikov wrote:

 Some one can complain that `foo()` returns pointers that are not
 available in CTFE but remember that the real code is more complex and
 `foo()` cam be return just length (`return b.get().length;`) having the
 same result.
I think the error you got is a bug and should be reported because it does not make sense: Error: couldn't find field `ar` of type `A[]` in `MapResult([0LU], null)`
 Basically I have a collection of data (`A[] ar`), different addressing
 through the indexes (multiple `size_t[] idx`) and I want to manipulate
 these objects through indexes. If there is another way to achieve the
 same in CFTE, I'd like to hear.
Have you considered std.range.indexed, which should at least cover the case of accessing: https://dlang.org/phobos/std_range.html#indexed For changing values, how about passing in a function pointer e.g. a lambda? The following one takes A and returns A but you can have a different approach where the function takes by 'ref A' and modifies its parameter: void set(A function(A) func) { foreach (i; idx) { ar[i] = func(ar[i]); } } Can you give a little more context for others to come up with a better answer? Ali
Nov 06 2021
parent reply Andrey Zherikov <andrey.zherikov gmail.com> writes:
On Saturday, 6 November 2021 at 13:57:47 UTC, Ali Çehreli wrote:
 Have you considered std.range.indexed, which should at least 
 cover the case of accessing:

   https://dlang.org/phobos/std_range.html#indexed
I don't need to iterate over whole collection, but subset of it.
 Can you give a little more context for others to come up with a 
 better answer?
I have a [CLI-parsing library](https://github.com/andrey-zherikov/argparse) that creates a set of argument objects based on UDAs. They are [stored in array](https://github.com/andrey-zherikov/argparse/blob/master/sou ce/argparse.d#L436) right now. I want to introduce some grouping having something like `struct Group { size_t[] args; }` but face the problem of addressing the objects. As of now I need to be able to iterate over the arguments within a group as well as those that satisfy some condition.
Nov 06 2021
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 11/6/21 8:57 AM, Andrey Zherikov wrote:
 On Saturday, 6 November 2021 at 13:57:47 UTC, Ali =C3=87ehreli wrote:
 Have you considered std.range.indexed, which should at least cover th=
e
 case of accessing:

   https://dlang.org/phobos/std_range.html#indexed
I don't need to iterate over whole collection, but subset of it.
'indexed' does not iterate the collection; just the ones that are in the = index array. For example, if you have 2 size_t elements in the index=20 array, 'indexed' will touch just two elements.
 As of now I need to be able to iterate over the arguments
 within a group as well as those that satisfy some condition.
It turns out 'indexed' provides access by reference; so you can even=20 mutate the elements: import std.algorithm; import std.range; import std.stdio; void main() { int[] numbers =3D [ 0, 1, 2, 3, 4, 5 ]; size_t[] specials =3D [ 1, 5 ]; size_t[] moreSpecials =3D [ 2, 3, 4 ]; numbers.indexed(specials).each!((ref n) =3D> n *=3D 10); writeln(numbers); } The output shows elements 1 and 5 are multiplied by 10: [0, 10, 2, 3, 4, 50] I am happy with that solution. ;) Of course you can use foreach instead of 'each' and apply some filtering = condition, etc. Ali
Nov 06 2021
prev sibling parent data pulverizer <data.pulverizer gmail.com> writes:
Hi,

It looks to me like `A*[]` would require extra allocation of 
memory and `ref` would not, am I wrong? If so it may be something 
to consider if relevant for your application.

Thanks.
Nov 06 2021