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

Reply via email to