Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Struct and Tuple template parameters in D

Is it possible to write a single templated function to increment the (numeric) fields of different structs? For example:

struct Color
{
    ubyte a,r,g,b;
}

struct Point
{
    double x, y;
}

I tried something like this:

T update(T, A)(T t, A a)
if (is(T == struct))
{
    auto vals = t.tupleof;
    foreach (i; 0 .. vals.length) {
        vals[i] += a; // error: i cannot be read at compile time
    }
    return T(vals); // convert back to struct
}

I have also tried writing function templates that accept tuples, but the tuples are always expanded, which prevents the compiler from matching the correct template. Thanks.

like image 456
YGL Avatar asked Jun 09 '12 01:06

YGL


1 Answers

Well, I'd say that what you're trying doing is rather bizarre, but it's certainly possible. The most naive, in-place way would probably be:

void update(T)(ref T t)
    if(is(T == struct))
{
    foreach(ref var; t.tupleof)
        ++var;
}

The simplest way to do it with a copy would probably be to copy it and then update it rather than trying to construct a new one with updated values (though I'm sure that that can be done too if you really want to):

T update(T)(T t)
    if(is(T == struct))
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
        ++var;

    return copy;
}

The main problem here, of course, is that the template constraint on both of these is far too weak. All you have to do is have unincrementable types in your struct, and it won't work. The simplest way to fix that would probably be to create an eponymous template to test it for you:

T update(T)(T t)
    if(isIncrementableStruct!T)
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
        ++var;

    return copy;
}

template isIncrementableStruct(T)
{
    enum isIncrementableStruct = is(T == struct) &&
                                 is(typeof({T t; foreach(var; t.tupleof) ++var;}));
}

And if you want to be able to increment all of the fields that are incrementable and leave the others alone, you'd probably do something like:

T update(T)(T t)
    if(is(T == struct))
{
    auto copy = t;

    foreach(ref var; copy.tupleof)
    {
        static if(canIncrement!(typeof(var)))
            ++var;
    }

    return copy;
}

template canIncrement(T)
{
    enum canIncrement = is(typeof({T var; ++var;}));
}

In any case, the main thing that you appear to have missed was to attempt iterating over tupleof directly while using ref so that the elements were updated rather than having copies of them being updated.

like image 175
Jonathan M Davis Avatar answered Sep 22 '22 07:09

Jonathan M Davis