Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annoying, transitive-const-ness issue in D

Tags:

constants

d

I'm running across a very annoying problem regarding transitive const in D.

I have the code below:

struct Slice(T)
{
    T items;
    size_t start, length, stride;

    this(T items, size_t start = 0, size_t length = size_t.max, size_t stride=1)
    {
        if (length == size_t.max)
        { length = 1 + (items.length - start - 1) / stride; }

        this.items = items;
        this.start = start;
        this.length = length;
        this.stride = stride;
    }

    Slice!(T) opSlice(size_t a, size_t b)
    {
        // Everything is fine here
        return Slice!(T)(items, start + a * stride, b - a, stride);
    }

    const(Slice!(T)) opSlice(size_t a, size_t b) const
    {
        // ERROR!  'items' is const(T), not T.
        return const(Slice!(T))(items, start + a * stride, b - a, stride);
    }
}

The trouble I'm running to is that, pretty much, the data types const(Slice!int) and Slice!const(int) and const(Slice!const(int)) are just... weird.

How do I overload opSlice above, to return a constant copy of the current slice which can subsequently be used like the original slice?

In other words, let's say I have:

void test(in Slice!(int[]) some_slice)
{
    //...
}

void main()
{
    auto my_slice = Slice!(int[])();
    const my_const_slice = my_slice;
    test(my_slice); // succeeds
    test(my_const_slice); //succeeds
    test(my_const_slice[0 .. 1]); // fails
}

The code above doesn't work. What is the best way of making it work? (I could of course always templatize test(), but then all the slice variations -- const(Slice!(Slice!const(int[]))) and such -- would grow exponentially, and confusingly so.)

Edit:

Is there a solution that works for structs and classes?

like image 306
user541686 Avatar asked Oct 30 '11 23:10

user541686


2 Answers

change the constructor to

inout this(inout T items, size_t start = 0, size_t length = size_t.max, size_t stride=1)
{
    if (length == size_t.max)
    { length = 1 + (items.length - start - 1) / stride; }

    this.items = items;
    this.start = start;
    this.length = length;
    this.stride = stride;
}

the inout keyword was made for this, it lets the const-ness/immutability of a parameter propagate to the result

like image 109
ratchet freak Avatar answered Sep 21 '22 15:09

ratchet freak


inout also works if Slice is a class:

class Slice(T)
{
    T items;
    size_t start, length, stride;
    this(){}
    inout this(inout T items, size_t start = 0, size_t length = size_t.max, size_t stride=1)
    {
        if (length == size_t.max)
            { length = 1 + (items.length - start - 1) / stride; }

        this.items = items;
        this.start = start;
        this.length = length;
        this.stride = stride;
    }

    inout(Slice!(T)) opSlice(size_t a, size_t b) inout{
        return new inout(Slice!T)(items, start + a * stride, b - a, stride);
    }
}

void test(in Slice!(int[]) some_slice)
{
    //...
}

void main()
{
    auto my_slice = new Slice!(int[])();
    const my_const_slice = my_slice;
    test(my_slice); // succeeds
    test(my_const_slice);//succeeds
    test(my_const_slice[0 .. 1]); // succeeds
}
like image 44
tgehr Avatar answered Sep 21 '22 15:09

tgehr