Generic structural recursion

H. S. Teoh hsteoh at quickfur.ath.cx
Wed Jan 26 19:54:35 UTC 2022


On Wed, Jan 26, 2022 at 06:41:50PM +0000, John Colvin via Digitalmars-d wrote:
> On Wednesday, 26 January 2022 at 18:22:18 UTC, H. S. Teoh wrote:
> > [...]
> 
> You could define a template that introspects a struct type and gives
> you a tuple of getters (each taking a struct and returning one of the
> doubles), then your functions that do real work would just loop over
> those getters. I suspect using the new(-ish) alias assign feature
> would help in defining that template.

Excellent idea!!  Instead of trying to inject some arbitrary function
with multiple arguments into the middle of a recursive traversal, do the
traversal separately at compile-time exclusively on the type, generate
an array of fields, and let the caller loop over them.

Here's the solution I settled on:

--------
string[] eachParam(T)(string prefix = "")
{
    string[] result;
    if (__ctfe)
    {
        foreach (field; FieldNameTuple!T)
        {
            alias F = typeof(__traits(getMember, T, field));
            static if (is(F == double))
            {
                result ~= prefix ~ "." ~ field;
            }
            else static if (is(F == struct))
            {
                result ~= eachParam!F(prefix ~ "." ~ field);
            }
            else static if (is(F == E[n], E, size_t n))
            {
                foreach (j; 0 .. __traits(getMember, T, field).length)
                {
                    result ~= eachParam!E(text(prefix, ".", field,
                                               "[", j, "]"));
                }
            }
        }
        return result;
    }
    else assert(0);
}

void interpolate(alias ipol = linearInterpolate, T)
                (ref T model, double idx, T t1, T t2)
{
    static foreach (param; eachParam!T)
    {
        mixin("model" ~ param) = ipol(idx, mixin("pose1" ~ param),
                                           mixin("pose2" ~ param));
    }
}

void nullify(ref T model)
{
    static foreach (param; eachParam!T)
    {
    	mixin("model" ~ param) = double.nan;
    }
}

// ... and so on
--------

Both parts nice and clean. Well OK, so I used a bunch of mixins. But
they are little self-contained snippets rather than entire function
bodies. That's a win.


T

-- 
Why ask rhetorical questions? -- JC


More information about the Digitalmars-d mailing list