Thread overview
Curious effect with traits, meta, and a foreach loop ... mystifies me.
Sep 07, 2021
james.p.leblanc
Sep 07, 2021
Adam D Ruppe
Sep 07, 2021
james.p.leblanc
Sep 08, 2021
Adam D Ruppe
Sep 09, 2021
Tejas
Sep 09, 2021
Tejas
Sep 10, 2021
james.p.leblanc
Sep 08, 2021
Bastiaan Veelo
September 07, 2021

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:

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

September 07, 2021
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.
September 07, 2021

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

September 08, 2021
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.
September 08, 2021

On Tuesday, 7 September 2021 at 17:24:34 UTC, james.p.leblanc wrote:

>
/*…*/

   // 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):

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.

September 09, 2021

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.

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?

September 09, 2021

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.

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

September 10, 2021

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