www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Dimensions in compile time

reply Vindex <tech.vindex gmail.com> writes:
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?
Feb 08 2021
parent reply Basile B. <b2.temp gmx.com> writes:
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.
Feb 08 2021
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
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.
Feb 08 2021
parent reply Vindex <tech.vindex gmail.com> writes:
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);
}
Feb 08 2021
parent Basile B. <b2.temp gmx.com> writes:
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".
Feb 08 2021
prev sibling parent reply Rumbu <rumbu rumbu.ro> writes:
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; }
Feb 08 2021
next sibling parent reply Basile B. <b2.temp gmx.com> writes:
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.
Feb 08 2021
parent Basile B. <b2.temp gmx.com> writes:
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.
Feb 08 2021
prev sibling parent Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
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.
Feb 08 2021