Thread overview
Store struct tuple of alias and access members through it?
Apr 07, 2018
Timoses
Apr 07, 2018
Simen Kjærås
Apr 19, 2018
Timoses
Apr 07, 2018
Alex
April 07, 2018
(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.
April 07, 2018
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
April 07, 2018
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;
    }
}
April 19, 2018
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.