On 05/29/2013 10:22 AM, Joseph Rushton Wakeling wrote:

> I've been having some trouble trying to work out how to effectively
> duplicate a multidimensional array in a way that preserves type qualifiers.

Templates preserve type qualifiers. So, as long as the return type is the same as the parameter type, then the type qualifiers should be preserved:

T foo(T)(T x)
{
    // ...
}

>     immutable int[][] x = [[10, 0, 0, 0],
>                            [0, 10, 0, 0],
>                            [0, 0, 10, 0],
>                            [0, 0, 0, 10]];
>     int[][] y = multidup(x);

As long as it is a conversion, std.conv.to handles that case for you:

import std.conv;
// ...
        int[][] y = x.to!(int[][]);

Done! :)

Unfortunately, std.conv.to is not a general copy tool. It does not do anything when converting to the same type. In other words, when the target type is the same as the source type it is a no-op, not a copy.

What do you think about the following recursive template solution? I have tested it only with arrays of int. :/

import std.stdio;
import std.traits;
import std.conv;

// This is for one-dimensional arrays
T multidup(T : E[], E)(T arr)
    if (!isArray!E)
{
    T result;

    static if (is (E == immutable)) {
        // No need to copy immutable elements
        result = arr;

    } else {
        // Otherwise, we must make a copy
        result = arr.dup;
    }

    return result;
}

// This is for array of array types
T multidup(T : E[], E)(T arr)
    if(isArray!E)
{
    T result;

    static if (is (E == immutable)) {
        // No need to go deeper than an immutable layer
        result == arr;

    } else {
        foreach(row; arr) {
            result ~= multidup(row);
        }
    }

    return result;
}

unittest
{
    {
        int[] a = [ 1, 2, 3 ];
        auto b = multidup(a);
        assert(typeid(b) is typeid(a));
        assert(a == b);
    }

    {
        immutable(int[]) a = [ 1, 2 ];
        auto b = multidup(a);
        assert(typeid(b) == typeid(immutable(int)[]));
        assert(a == b);
        assert(a.ptr == b.ptr);
    }

    {
        immutable(int)[] a = [ 1, 2, 3 ];
        auto b = multidup(a);
        assert(typeid(b) == typeid(a));
        assert(a == b);
        assert(a.ptr == b.ptr);
        // assert(a[0].ptr == b[0].ptr);
    }

    {
        immutable(int)[][] a = [ [ 1, 2, 3 ],
                                 [ 4, 5, 6 ],
                                 [ 7, 8, 9 ] ];
        auto b = multidup(a);
        assert(typeid(b) == typeid(a));
        assert(a == b);
        assert(a.ptr != b.ptr);
        foreach (i; 0 .. a.length) {
            assert(a[i].ptr == b[i].ptr);
        }
    }

    {
        alias Elem = const(int[])[];
        const(int[])[][]a = [ [ [ 1, 2, 3 ],
                                [ 4, 5, 6 ],
                                [ 7, 8, 9 ] ],

                              [ [ 1, 2, 3 ],
                                [ 4, 5, 6 ],
                                [ 7, 8, 9 ] ] ];

        auto b = multidup(a);
        assert(typeid(b) == typeid(a));
        assert(a == b);
        assert(a.ptr != b.ptr);
        foreach (i; 0 .. a.length) {
            assert(a[i].ptr != b[i].ptr);
        }
    }
}

void main()
{}

Is it usable in your situation?

Ali

Reply via email to