www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Curious effect with traits, meta, and a foreach loop ... mystifies me.

reply james.p.leblanc <james.p.leblanc gmail.com> writes:
Dear All,

In playing with some reflection and meta programming, this 
curiosity
appeared.

Does someone understand what is happening?  I would appreciate 
learning
about it if possible.  Enclosed code snippet tells the story:

```d
import std.stdio;
import std.traits;
import std.meta;

struct  S0{ double[3] x; };
struct  S1{ double junk0 ; double[3] x; };

union U { S0 s0;  S1 s1; }

void main(){

    U u;

    double* ptr;

    u.s0.x = [ 0.0, 0.1, 0.3 ];
    u.s1.x = [ 1.0, 1.1, 1.3 ];

    writeln("typeid( u.tupleof): ",    typeid( u.tupleof ) );
    writeln("typeid( u.tupleof[0]): ", typeid( u.tupleof[0] ) );
    writeln("typeid( u.tupleof[0].x): ", typeid( u.tupleof[0].x ) 
);

    writeln();

    // indeed both tuples exist
    writeln("u.tupleof[0].x.ptr: ", u.tupleof[0].x.ptr  );
    writeln("u.tupleof[1].x.ptr: ", u.tupleof[1].x.ptr  );

    // this is fine (notice that 'val' is never used
    foreach( i, val ; u.tupleof ){
       ptr = u.tupleof[i].x.ptr;
       writeln("ptr: ", ptr);
    }

    // this fails with: "Error: variable 'i' cannot be read at 
compile time
    //
    // foreach( i ; 0 .. 3 ){
    //    ptr = u.tupleof[i].x.ptr;
    //    writeln("ptr: ", ptr);
    // }
}

```

Best Regards,
James
Sep 07 2021
next sibling parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Tuesday, 7 September 2021 at 17:24:34 UTC, james.p.leblanc 
wrote:
    // this fails with: "Error: variable 'i' cannot be read at 
 compile time
    //
    // foreach( i ; 0 .. 3 ){
    //    ptr = u.tupleof[i].x.ptr;
tuples only exist at compile time, so you'd have to make sure the indexing is itself compile time. Consider that unlike an array, each index might give a different type, so like what would struct A { int a; string b; } A a; int idx; some_type c = a.tupleof[idx]; Which type is some_type? Is it int or string? Impossible to tell since it doesn't know what idx is. So idx needs to be known at compile time so it knows which type you get there. The reason why it works here: foreach( i, val ; u.tupleof ){ is because the compiler knows you're specifically looping over the tupleof, so it expands it and knows what i is going to be at compile time. If you assigned that i to an intermediate variable then it would break this direct knowledge and it doesn't compile again. If you want to do a runtime lookup, you need to separate the two pieces. This pattern works: switch(runtime_index) { foreach(i, val; item.tupleof) case i: // use val } So the switch is at runtime but the loop and cases are all known at compile time.
Sep 07 2021
parent reply james.p.leblanc <james.p.leblanc gmail.com> writes:
On Tuesday, 7 September 2021 at 17:33:31 UTC, Adam D Ruppe wrote:
 On Tuesday, 7 September 2021 at 17:24:34 UTC, james.p.leblanc 
 wrote:

 If you want to do a runtime lookup, you need to separate the 
 two pieces. This pattern works:


 switch(runtime_index) {
    foreach(i, val; item.tupleof)
      case i:
            // use val
 }

 So the switch is at runtime but the loop and cases are all 
 known at compile time.
Adam, Thanks for the very fast, and very thorough explanation. I especially appreciate the fact that you seem to have predicted where my thoughts were heading with my experiments ... The "switch(runtime_index)" snippet will come in handy ... What I would **REALLY** like is to be able to do (but I think this is impossible) would be to "dig out" the needed "x" array depending on which one of them suits my alignment needs. (Yes, I am still playing with avx2 ideas ...). What I mean by "dig out" the needed "x" is: if I could alias/enum/ or someother trick be then able just to use that "x" as a simple static array. (I doubt this is possible ... but .... ?). Thanks again, Keep Warm in Upstate! James
Sep 07 2021
next sibling parent Adam D Ruppe <destructionator gmail.com> writes:
On Tuesday, 7 September 2021 at 17:47:15 UTC, james.p.leblanc 
wrote:
 What I mean by "dig out" the needed "x" is:  if I could 
 alias/enum/
 or someother  trick be then able just to use that "x" as a 
 simple static array.
You might be able to just cast the struct to a static array of the same size if the types are all compatible. Like a reinterpret cast of the raw memory kind of idea. struct A { int a; int b; } void main() { A a; int[2] as_array = cast(int[2]) a; } That works. But idk if it will help with your alignment issue, I don't know much about avx at all.
 Thanks again, Keep Warm in Upstate!
It has actually been kinda nice the last few days! Winter coming soon though, sigh.
Sep 08 2021
prev sibling parent reply Tejas <notrealemail gmail.com> writes:
On Tuesday, 7 September 2021 at 17:47:15 UTC, james.p.leblanc 
wrote:
 On Tuesday, 7 September 2021 at 17:33:31 UTC, Adam D Ruppe 
 wrote:
 On Tuesday, 7 September 2021 at 17:24:34 UTC, james.p.leblanc 
 wrote:

 If you want to do a runtime lookup, you need to separate the 
 two pieces. This pattern works:


 switch(runtime_index) {
    foreach(i, val; item.tupleof)
      case i:
            // use val
 }

 So the switch is at runtime but the loop and cases are all 
 known at compile time.
Adam, Thanks for the very fast, and very thorough explanation. I especially appreciate the fact that you seem to have predicted where my thoughts were heading with my experiments ... The "switch(runtime_index)" snippet will come in handy ... What I would **REALLY** like is to be able to do (but I think this is impossible) would be to "dig out" the needed "x" array depending on which one of them suits my alignment needs. (Yes, I am still playing with avx2 ideas ...). What I mean by "dig out" the needed "x" is: if I could alias/enum/ or someother trick be then able just to use that "x" as a simple static array. (I doubt this is possible ... but .... ?). Thanks again, Keep Warm in Upstate! James
from what I understand you want to change the aligned data that you're referring to at runtime. ```d void main() { import std.experimental.allocator.mallocator; import std.stdio: write, writeln, writef, writefln, readf; uint alignment, length; readf!"%u %u"(length,alignment); auto buffer = AlignedMallocator.instance.alignedAllocate(length, alignment); writeln(&buffer[0]); scope(exit) AlignedMallocator.instance.deallocate(buffer); //... } ``` Is this it?
Sep 08 2021
parent reply Tejas <notrealemail gmail.com> writes:
On Thursday, 9 September 2021 at 05:32:29 UTC, Tejas wrote:
 On Tuesday, 7 September 2021 at 17:47:15 UTC, james.p.leblanc 
 wrote:
 [...]
from what I understand you want to change the aligned data that you're referring to at runtime. ```d void main() { import std.experimental.allocator.mallocator; import std.stdio: write, writeln, writef, writefln, readf; uint alignment, length; readf!"%u %u"(length,alignment); auto buffer = AlignedMallocator.instance.alignedAllocate(length, alignment); writeln(&buffer[0]); scope(exit) AlignedMallocator.instance.deallocate(buffer); //... } ``` Is this it?
Also, link : https://dlang.org/phobos/std_experimental_allocator_mallocator.html#.Mallocator.reallocate
Sep 08 2021
parent james.p.leblanc <james.p.leblanc gmail.com> writes:
On Thursday, 9 September 2021 at 05:37:35 UTC, Tejas wrote:
 On Thursday, 9 September 2021 at 05:32:29 UTC, Tejas wrote:
 On Tuesday, 7 September 2021 at 17:47:15 UTC, james.p.leblanc 
 wrote:
 [...]
writeln(&buffer[0]);
     scope(exit) AlignedMallocator.instance.deallocate(buffer);
     //...


 }
 ```

 Is this it?
Also, link : https://dlang.org/phobos/std_experimental_allocator_mallocator.html#.Mallocator.reallocate
Adam, Tejas, Thanks for all of your kind suggestions (the struct wrapper -as_array, runtime switch), and (AlignedMallocator). All of these move me closer to a solution. Moreover, the suggestions give me a broader perspective. So I very much appreciate the time and effort volunteered by the dlang community to help newcomers such as myself. Best Regards, James
Sep 09 2021
prev sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Tuesday, 7 September 2021 at 17:24:34 UTC, james.p.leblanc 
wrote:
 ```d
 /*…*/

    // this is fine (notice that 'val' is never used
    foreach( i, val ; u.tupleof ){
       ptr = u.tupleof[i].x.ptr;
       writeln("ptr: ", ptr);
    }

    // this fails with: "Error: variable 'i' cannot be read at 
 compile time
    //
    // foreach( i ; 0 .. 3 ){
    //    ptr = u.tupleof[i].x.ptr;
    //    writeln("ptr: ", ptr);
    // }
 }

 ```
As Adam mentioned `tupleof` only exists at compile time, and a `foreach` over a `tupleof` gets unrolled at compile time, akin to a `static foreach`. Consequently you can make your snippet work by prepending `static` (and fixing the range): ```d static foreach (i; 0 .. u.tupleof.length) { ptr = u.tupleof[i].x.ptr; writeln("ptr: ", ptr); } ``` https://run.dlang.io/is/T6jrjf Not sure if that helps in what you’re trying to achieve though, as that isn’t clear to me. —Bastiaan.
Sep 08 2021