Generic structural recursion

Steven Schveighoffer schveiguy at gmail.com
Wed Jan 26 19:06:18 UTC 2022


On 1/26/22 1:22 PM, H. S. Teoh wrote:

> --------
> X interpolate(X)(ref X result, double idx, X data1, X data2) {
> 	foreach (field; FieldNameTuple!X) {
> 		alias Type = typeof(__traits(getMember, X, field));
> 		if (is(Type == double))
> 			__traits(getMember, result, field) =
> 				(1.0-idx)*__traits(getMember, data1, field)
> 				+ idx*__traits(getMember, data2, field);
> 		else if (is(Type == struct)) {
> 			interpolate(
> 				__traits(getMember, result, field), idx,
> 				__traits(getMember, data1, field),
> 				__traits(getMember, data2, field));
> 		} else if (is(Type == U[n], U, size_t n)) {
> 			foreach (i; 0 .. n) {
> 				interpolate(
> 					__traits(getMember, result, field)[i], idx,
> 					__traits(getMember, data1, field)[i],
> 					__traits(getMember, data2, field)[i]);
> 			}
> 		}
> 	}
> }
> --------

This looks a lot like serialization. What you have done looks more 
specific to your use case though. Why not call a function recursively 
for each type encountered?

e.g.:

```d
auto interpolate(alias fn, X : double)(ref X val1, ref X val2)
{
    static if(is(typeof(fn(val1, val2)))) return fn(val1, val2);
}

auto interpolate(alias fn, X)(ref X val1, ref X val2) if (is(X == struct))
{
    // loop and recurse
}
...
```

Then you can write a set of templates that act on the right types, and 
do the thing you want for that specific type, pass that in as `fn`.

I find splitting the work into distinct portions when doing these kinds 
of introspection tasks to be more straightforward and testable.

-Steve


More information about the Digitalmars-d mailing list