www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Store struct tuple of alias and access members through it?

reply Timoses <timosesu gmail.com> writes:
(Please read at the very bottom what I'd like to achieve)

Is it possible to return the member of a struct by its .tupleof 
index?

I know that it would work on a struct value, but I'd like it to 
work on the type's tupleof:

```
struct S { int i;}
S s;
// below leads to: Error: need this for s1 of type uint
// writeln(/*somehow access s via the S tupleof? */ S.tupleof[0]);
// vs
writeln(s.tupleof[0]);
```

See below example to make the intention a bit clearer:

https://run.dlang.io/gist/6fdb01ddd78b14f8b9a94ac951580cb8
```
struct S
{
     uint s1;
     ushort s2;
}

interface IParam
{}

template Param(T)
{
     static if (isBasicType!T)
         alias members = AliasSeq!();
     else
         alias members = AliasSeq!(T.tupleof);

     class Param : IParam
     {
         T m;
         this(T m)
         {
             this.m = m;
         }

         IParam opIndex(size_t i)
         {
             // Something like this possible?????
             // return this.m.members[i];       // <------------ 
how???

             // This works but feels needless.
             static foreach (j, t; members)
                 if (i == j)
             	{
                 	return new 
Param!(typeof(members[j]))(__traits(getMember, this.m, 
members[j].stringof));          // <------------- 
members[j].stringof feels ugly just to get the member that should 
be stored in 'members' already...
            		}
             return null;
         }
     }
}
```

The reason why I don't want `m.tupleof[i]` is because later I'd 
like to consider bitfields within the struct. This means I'd have 
to also consider the member functions of the struct and 
potentially return them.

E.g.

```
struct S
{
     int s1;
     int s2() { return 3; }
}
```

and then I'd like to have
alias members = (s1, s2) // pseudo code..

so I could return
S s;
s.members[1]; // would evaluate the function s2 and return the 
value

----------

In the end I would like to accomplish the following:
Provide access to contained bitfields and members of a struct in 
the order they
appear in the struct via an index.

I hope I made a somewhat decent job in explaining what I'm trying 
to accomplish.

Please let me know if anything is unclear.
Apr 07 2018
next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Saturday, 7 April 2018 at 13:31:01 UTC, Timoses wrote:
 In the end I would like to accomplish the following:
 Provide access to contained bitfields and members of a struct 
 in the order they
 appear in the struct via an index.
The behavior of Type.tupleof in D seems a bit unfinished - they can't be passed to other functions, they can't be directly used to get the member they refer to, and the indirect way is clunky. Anyways. Your desired code `return this.m.members[i];` is, as you have noticed, impossible. There's multiple reasons for that - first, `members` can't be used like that. Second, since you need to wrap it in a Param instance, you need more information than that. Third, there's a clear distinction in D between compile-time and run-time values, so you need the static foreach there to get the right compile-time value. Now, what *can* we do? First, there's no need for __traits(getMember, this.m, members[j].stringof), since the index into T.tupleof is the exact same as for m.tupleof. In other words, you could use return new Param!(typeof(m.tupleof[j]))(m.tupleof[j]); I think that is clearer. I'd suggest also creating this function: IParam param(T)(T value) { return new Param!T(value); } That way, the above line would be return param(m.tupleof[j]); Handling methods is a tad more complicated, and you will not get the correct interleaving of methods and fields, which may or may not be a problem to you. Here's my attempt at solving all your problems. There may be things I've misunderstood, forgotten or ignored, and there are certainly places where I'm unsure. import std.meta; import std.traits; // List all member functions, and wrap them such that myFoo.fun(3) can be called as AllMemberFunctions!(typeof(myFoo))[idx](myFoo, 3). template AllMemberFunctions(T) { template createDg(alias fn) { static if (__traits(isStaticFunction, fn)) alias createDg = fn; else ReturnType!fn createDg(ref T ctx, Parameters!fn args) { ReturnType!fn delegate(Parameters!fn) fun; fun.funcptr = &fn; fun.ptr = cast(void*)&ctx; return fun(args); } } alias GetOverloads(string name) = AliasSeq!(__traits(getOverloads, T, name)); alias AllMemberFunctions = staticMap!(createDg, staticMap!(GetOverloads, __traits(allMembers, T))); } interface IParam { // Moved this here, since otherwise you'd need to know the // exact template parameters to Param to get to anything. IParam opIndex(size_t i); } // Simplified template definition. class Param(T) : IParam { T m; this(T m) { this.m = m; } static if (!isBasicType!T && !isArray!T && !isFunctionPointer!T) { IParam opIndex(size_t i) { switch (i) { // Go through all members: static foreach (j; 0..m.tupleof.length) case j: return param(m.tupleof[j]); // Then all functions after: static foreach (j, fn; AllMemberFunctions!T) case j+m.tupleof.length: return param(&fn); // And blow up if the index is invalid. default: assert(false, "Invalid index!"); } } } else { IParam opIndex(size_t i) { assert(false, T.stringof ~ " is not an aggregate type, and can't be indexed."); } } } IParam param(T)(T value) { return new Param!T(value); } struct S { int n; float f; string s; int fn() { return n+2; } string fun() { return ""; } string fun(int n) { return ""; } static void func() {} } unittest { S s; IParam a = param(s); } -- Simen
Apr 07 2018
parent Timoses <timosesu gmail.com> writes:
On Saturday, 7 April 2018 at 19:21:30 UTC, Simen Kjærås wrote:
 import std.meta;
 import std.traits;

 // List all member functions, and wrap them such that 
 myFoo.fun(3) can be called as 
 AllMemberFunctions!(typeof(myFoo))[idx](myFoo, 3).
 template AllMemberFunctions(T)
 {
     template createDg(alias fn)
     {
         static if (__traits(isStaticFunction, fn))
             alias createDg = fn;
         else
             ReturnType!fn createDg(ref T ctx, Parameters!fn 
 args)
             {
                 ReturnType!fn delegate(Parameters!fn) fun;
                 fun.funcptr = &fn;
                 fun.ptr = cast(void*)&ctx;
                 return fun(args);
             }
     }

     alias GetOverloads(string name) = 
 AliasSeq!(__traits(getOverloads, T, name));

     alias AllMemberFunctions = staticMap!(createDg, 
 staticMap!(GetOverloads, __traits(allMembers, T)));
 }

 --
   Simen
Many thanks for this!!! Was really helpful. I ended up unfolding the struct members into an array of member strings and mapping those to either the struct tuple members or the struct function members. This way I can call all members (normal and bitfield members) in order. Result: https://gist.github.com/Timoses/c78e599e91b8d05be34aefaf75ca3739 This project is really teaching me some template actions : D.
Apr 19 2018
prev sibling parent Alex <sascha.orlov gmail.com> writes:
On Saturday, 7 April 2018 at 13:31:01 UTC, Timoses wrote:

Simen was faster :)

In my solution I simply ignore such things as functions... But 
there is the cool delegate creation approach in Simen's solution 
for this. I can handle arrays instead. :)
And I got rid of tupelof acting on an instance.

Be aware, that bitfields create more fields then the delegates 
for the bitfield's members...

import std.stdio;
import std.bitmanip;
import std.traits;

void main()
{
     S s;
     Param!S example = new Param!S(s);

     writeln(example[0]);
     writeln(example[1]);
     writeln(example[2]);
     writeln(example[3]);
     writeln(example[4]);
     writeln(example[5]);
     writeln(example[6]);

     writeln([__traits(allMembers, S)]);

     writeln(example[15]);
     writeln(example[16]);
     writeln(example[17]);
     writeln(example[18]);
}

struct S
{
     uint s1;
     ushort s2;

     string s3;
     float s4;

     mixin(bitfields!(
         uint, "x",    2,
         int,  "y",    3,
         uint, "z",    2,
         bool, "flag", 1));

     size_t fun(){ return 42; }

     size_t delegate() dg;

     size_t[] arr;

     static void fun(){}
}

interface IParam{}

class Param(T) : IParam
{
     T m;
     this(T m)
     {
         this.m = m;
     }

     IParam opIndex(size_t i)
     {
         //static if(!isBasicType!T)
         static if(__traits(compiles, __traits(allMembers, T)))
         {
         	static foreach (j, t; __traits(allMembers, T))
         	{
         		if (i == j)
             	{
             		static if(__traits(compiles, new 
Param!(typeof(__traits(getMember, this.m, 
t)))(__traits(getMember, this.m, t))))
             		{
             			return new Param!(typeof(__traits(getMember, 
this.m, t)))(__traits(getMember, this.m, t));
             		}
            		}
         	}
         }
         return null;
     }
}
Apr 07 2018