Thread overview
Dimensions in compile time
Feb 08, 2021
Vindex
Feb 08, 2021
Basile B.
Feb 08, 2021
Basile B.
Feb 08, 2021
Vindex
Feb 08, 2021
Basile B.
Feb 08, 2021
Rumbu
Feb 08, 2021
Basile B.
Feb 08, 2021
Basile B.
February 08, 2021
size_t ndim(A)(A arr) {
    return std.algorithm.count(typeid(A).to!string, '[');
}

Is there a way to find out the number of dimensions in an array at compile time?
February 08, 2021
On Monday, 8 February 2021 at 11:42:45 UTC, Vindex wrote:
> size_t ndim(A)(A arr) {
>     return std.algorithm.count(typeid(A).to!string, '[');
> }
>
> Is there a way to find out the number of dimensions in an array at compile time?

yeah.

---
template dimensionCount(T)
{
    static if (isArray!T)
    {
        static if (isMultiDimensionalArray!T)
        {
            alias DT = typeof(T.init[0]);
            enum dimensionCount = dimensionCount!DT + 1;
        }
        else enum dimensionCount = 1;
    }
    else enum dimensionCount = 0;
}
///
unittest
{
    static assert(dimensionCount!char == 0);
    static assert(dimensionCount!(string[]) == 1);
    static assert(dimensionCount!(int[]) == 1);
    static assert(dimensionCount!(int[][]) == 2);
    static assert(dimensionCount!(int[][][]) == 3);
}
---

that can be rewritten using some phobos traits too I think, but this piece of code is very old now, more like learner template.
February 08, 2021
On Monday, 8 February 2021 at 12:19:26 UTC, Basile B. wrote:
> On Monday, 8 February 2021 at 11:42:45 UTC, Vindex wrote:
>> [...]
>
> yeah.
>
> ---
> template dimensionCount(T)
> {
>     static if (isArray!T)
>     {
>         static if (isMultiDimensionalArray!T)
>         {
>             alias DT = typeof(T.init[0]);
>             enum dimensionCount = dimensionCount!DT + 1;
>         }
>         else enum dimensionCount = 1;
>     }
>     else enum dimensionCount = 0;
> }
> ///
> unittest
> {
>     static assert(dimensionCount!char == 0);
>     static assert(dimensionCount!(string[]) == 1);
>     static assert(dimensionCount!(int[]) == 1);
>     static assert(dimensionCount!(int[][]) == 2);
>     static assert(dimensionCount!(int[][][]) == 3);
> }
> ---
>
> that can be rewritten using some phobos traits too I think, but this piece of code is very old now, more like learner template.

well I didn't realize but static array are not handled. I think this is because in first place this was made for a specific usage that was a serializer.
February 08, 2021
On Monday, 8 February 2021 at 12:19:26 UTC, Basile B. wrote:
> On Monday, 8 February 2021 at 11:42:45 UTC, Vindex wrote:
>> size_t ndim(A)(A arr) {
>>     return std.algorithm.count(typeid(A).to!string, '[');
>> }
>>
>> Is there a way to find out the number of dimensions in an array at compile time?
>
> yeah.
>
> ---
> template dimensionCount(T)
> {
>     static if (isArray!T)
>     {
>         static if (isMultiDimensionalArray!T)
>         {
>             alias DT = typeof(T.init[0]);
>             enum dimensionCount = dimensionCount!DT + 1;
>         }
>         else enum dimensionCount = 1;
>     }
>     else enum dimensionCount = 0;
> }
> ///
> unittest
> {
>     static assert(dimensionCount!char == 0);
>     static assert(dimensionCount!(string[]) == 1);
>     static assert(dimensionCount!(int[]) == 1);
>     static assert(dimensionCount!(int[][]) == 2);
>     static assert(dimensionCount!(int[][][]) == 3);
> }
> ---
>
> that can be rewritten using some phobos traits too I think, but this piece of code is very old now, more like learner template.

dimensionCount!string should be 2.

My take without std.traits:

template rank(T: U[], U)
{
   enum rank = 1 + rank!U;
}

template rank(T: U[n], size_t n)
{
    enum rank = 1 + rank!U;
}

template rank(T)
{
    enum rank = 0;
}


February 08, 2021
On Monday, 8 February 2021 at 13:09:53 UTC, Rumbu wrote:
> On Monday, 8 February 2021 at 12:19:26 UTC, Basile B. wrote:
>> [...]
>
> dimensionCount!string should be 2.
>
> My take without std.traits:
>
> template rank(T: U[], U)
> {
>    enum rank = 1 + rank!U;
> }
>
> template rank(T: U[n], size_t n)
> {
>     enum rank = 1 + rank!U;
> }
>
> template rank(T)
> {
>     enum rank = 0;
> }

yeah you're right, strings were a special case (I remember now) so that stuff could be stored as literals.
February 08, 2021
On Monday, 8 February 2021 at 13:13:33 UTC, Basile B. wrote:
> On Monday, 8 February 2021 at 13:09:53 UTC, Rumbu wrote:
>> On Monday, 8 February 2021 at 12:19:26 UTC, Basile B. wrote:
>>> [...]
>>
>> dimensionCount!string should be 2.
>>
>> My take without std.traits:
>>
>> template rank(T: U[], U)
>> {
>>    enum rank = 1 + rank!U;
>> }
>>
>> template rank(T: U[n], size_t n)
>> {
>>     enum rank = 1 + rank!U;
>> }
>>
>> template rank(T)
>> {
>>     enum rank = 0;
>> }
>
> yeah you're right, strings were a special case (I remember now) so that stuff could be stored as literals.

well to OP just dont use my dimensionCount template ^^. it should have been declared with the "package" protection in first place; not public.
February 08, 2021
Thanks everyone!

The solution that works for me now looks like this:


template ndim(T) {
    static if (std.traits.isArray!T) {
        static if (is(typeof(T.init[0]))) {
            alias SubArrayType = typeof(T.init[0]);
            enum ndim = ndim!SubArrayType + 1;
        }
        else enum ndim = 1;
    }
    else enum ndim = 0;
}
unittest {
    assert(ndim!(int[]) == 1);
    assert(ndim!(int[][]) == 2);
    assert(ndim!(int[4][3]) == 2);
    assert(ndim!(int[][2][]) == 3);
    assert(ndim!(string) == 1);
}

February 08, 2021
On Monday, 8 February 2021 at 13:27:14 UTC, Vindex wrote:
> Thanks everyone!
>
> The solution that works for me now looks like this:
>
>
> template ndim(T) {
>     static if (std.traits.isArray!T) {
>         static if (is(typeof(T.init[0]))) {
>             alias SubArrayType = typeof(T.init[0]);
>             enum ndim = ndim!SubArrayType + 1;
>         }
>         else enum ndim = 1;
>     }
>     else enum ndim = 0;
> }
> unittest {
>     assert(ndim!(int[]) == 1);
>     assert(ndim!(int[][]) == 2);
>     assert(ndim!(int[4][3]) == 2);
>     assert(ndim!(int[][2][]) == 3);
>     assert(ndim!(string) == 1);
> }

Nice. You should look at Rumbu solution too. It possibly has a better behavior toward the recursive expansion. Also interesting to learn the more advanced use of "is".
February 08, 2021
On Monday, 8 February 2021 at 13:09:53 UTC, Rumbu wrote:
> On Monday, 8 February 2021 at 12:19:26 UTC, Basile B. wrote:
>> On Monday, 8 February 2021 at 11:42:45 UTC, Vindex wrote:
>>> size_t ndim(A)(A arr) {
>>>     return std.algorithm.count(typeid(A).to!string, '[');
>>> }
>>>
>>> Is there a way to find out the number of dimensions in an array at compile time?
>>
>> yeah.
>>
>> ---
>> template dimensionCount(T)
>> {
>>     static if (isArray!T)
>>     {
>>         static if (isMultiDimensionalArray!T)
>>         {
>>             alias DT = typeof(T.init[0]);
>>             enum dimensionCount = dimensionCount!DT + 1;
>>         }
>>         else enum dimensionCount = 1;
>>     }
>>     else enum dimensionCount = 0;
>> }
>> ///
>> unittest
>> {
>>     static assert(dimensionCount!char == 0);
>>     static assert(dimensionCount!(string[]) == 1);
>>     static assert(dimensionCount!(int[]) == 1);
>>     static assert(dimensionCount!(int[][]) == 2);
>>     static assert(dimensionCount!(int[][][]) == 3);
>> }
>> ---
>>
>> that can be rewritten using some phobos traits too I think, but this piece of code is very old now, more like learner template.
>
> dimensionCount!string should be 2.
>
> My take without std.traits:
>
> template rank(T: U[], U)
> {
>    enum rank = 1 + rank!U;
> }
>
> template rank(T: U[n], size_t n)
> {
>     enum rank = 1 + rank!U;
> }
>
> template rank(T)
> {
>     enum rank = 0;
> }

Here's the version I actually wanted to write:

---
enum rank(T) = is(T : U[], U) ? 1 + rank!U : 0;
---

But it's not possible, because of 2 language limitations:
1. Ternary operator doesn't allow the different branches to be specialized like `static if` even if the condition is a compile-time constant.
2. `is()` expressions can only introduce an identifier if inside a `static if`.

Otherwise, I'd consider this the "idiomatic" / "typical" D solution, since unlike C++, D code rarely (*) overloads and specializes templates.

(*) Modern Phobos(-like) code.

---
template rank(T)
{
    static if (is(T : U[], U))
        enum rank = 1 + rank!U;
    else
        enum rank = 0;
}

unittest
{
    static assert( rank!(char) == 0);
    static assert( rank!(char[]) == 1);
    static assert( rank!(string) == 1);
    static assert( rank!(string[]) == 2);
    static assert( rank!(string[][]) == 3);
    static assert( rank!(string[][][]) == 4);
}
---


Otherwise, the shortest and cleanest solution IMO is this one:

---
enum rank(T : U[], U) = is(T : U[], U) ? 1 + rank!U : 0;
enum rank(T) = 0;

unittest
{
    static assert( rank!(char) == 0);
    static assert( rank!(char[]) == 1);
    static assert( rank!(string) == 1);
    static assert( rank!(string[]) == 2);
    static assert( rank!(string[][]) == 3);
    static assert( rank!(string[][][]) == 4);

    static assert( rank!(char) == 0);
    static assert( rank!(char[1]) == 1);
    static assert( rank!(char[1][2]) == 2);
    static assert( rank!(char[1][2][3]) == 3);
    static assert( rank!(char[1][2][3][4]) == 4);
}
---

- Use eponymous template syntax shorthand
- Static arrays are implicitly convertible to dynamic arrays, so we can merge the two implementations.