www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - inferred size for static array initialization

reply Erik Smith <erik cruiserhouse.com> writes:
Is there a way to initialize a static array and have it's size 
inferred (and that works for arrays of structs using braced 
literals)?  This would make it easier to maintain longer static 
array definitions.  The code below doesn't work when removing the 
array size even though the array is declared as static immutable.

     import std.traits;
     static immutable int[] a  = [1,2,3];
     static assert(isStaticArray!(typeof(a)));  // fails
May 02 2016
next sibling parent reply Basile B <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 13:00:27 UTC, Erik Smith wrote:
 Is there a way to initialize a static array and have it's size 
 inferred (and that works for arrays of structs using braced 
 literals)?  This would make it easier to maintain longer static 
 array definitions.  The code below doesn't work when removing 
 the array size even though the array is declared as static 
 immutable.

     import std.traits;
     static immutable int[] a  = [1,2,3];
     static assert(isStaticArray!(typeof(a)));  // fails
Help yourself with a template: ---- import std.traits; auto toStaticArray(alias array)() if (isArray!(typeof(array))) { enum size = array.length; alias T = typeof(array.init[0])[size]; T result = array[0..size]; return result; } enum a = toStaticArray!([1,2,3]); static assert(isStaticArray!(typeof(a))); // success ---- Does it fit ?
May 02 2016
parent Basile B <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 13:22:01 UTC, Basile B wrote:
 On Monday, 2 May 2016 at 13:00:27 UTC, Erik Smith wrote:
 Is there a way to initialize a static array and have it's size 
 inferred (and that works for arrays of structs using braced 
 literals)?  This would make it easier to maintain longer 
 static array definitions.  The code below doesn't work when 
 removing the array size even though the array is declared as 
 static immutable.

     import std.traits;
     static immutable int[] a  = [1,2,3];
     static assert(isStaticArray!(typeof(a)));  // fails
Help yourself with a template: ---- import std.traits; auto toStaticArray(alias array)() if (isArray!(typeof(array))) { enum size = array.length; alias T = typeof(array.init[0])[size]; T result = array[0..size]; return result; } enum a = toStaticArray!([1,2,3]); static assert(isStaticArray!(typeof(a))); // success ---- Does it fit ?
Using an enum is probably a bit better ---- auto toStaticArray(alias array)() if (isDynamicArray!(typeof(array)) && array.length) { alias T = typeof(array[0])[array.length]; enum T result = array[0..array.length]; return result; }
May 02 2016
prev sibling next sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Mon, 02 May 2016 13:00:27 +0000
schrieb Erik Smith <erik cruiserhouse.com>:

 Is there a way to initialize a static array and have it's size 
 inferred (and that works for arrays of structs using braced 
 literals)?  This would make it easier to maintain longer static 
 array definitions.  The code below doesn't work when removing the 
 array size even though the array is declared as static immutable.
 
      import std.traits;
      static immutable int[] a  = [1,2,3];
      static assert(isStaticArray!(typeof(a)));  // fails
 
Sure, struct S { int a, b; } immutable tab = { static enum S[] s = [ {1,2}, {3,4}, ]; return cast(typeof(s[0])[s.length])s; }(); static assert(isStaticArray!(typeof(tab))); // succeeds -- Marco
May 02 2016
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 02.05.2016 15:53, Marco Leise wrote:
    immutable tab = { static enum S[] s = [
`static enum`? What kind of black magic is this?
May 02 2016
parent Marco Leise <Marco.Leise gmx.de> writes:
Am Mon, 2 May 2016 18:52:11 +0200
schrieb ag0aep6g <anonymous example.com>:

 On 02.05.2016 15:53, Marco Leise wrote:
    immutable tab = { static enum S[] s = [  
`static enum`? What kind of black magic is this?
I don't know, but it works, haha. -- Marco
May 02 2016
prev sibling parent Erik Smith <erik cruiserhouse.com> writes:
I tried to combine the two solutions (Basile with the wrapper, 
Marco with the struct initializer support) but it didn't work. 
The struct initializer is not a array literal (seems obvious 
now).   I might go with the 2nd but it's pretty heavy just to get 
the size.

Thanks.


struct S {
     int a, b;
}

auto toStaticArray(alias array)()
{
     struct S { int a, b; }

     immutable tab = {
         static enum S[] s = array;
         return cast(typeof(s[0])[s.length])s;
     }();
     return tab;
}

enum a = toStaticArray!([{1,2},{3,4}]);  // error
May 02 2016
prev sibling parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 2 May 2016 at 13:00:27 UTC, Erik Smith wrote:
 Is there a way to initialize a static array and have it's size 
 inferred (and that works for arrays of structs using braced 
 literals)?  This would make it easier to maintain longer static 
 array definitions.  The code below doesn't work when removing 
 the array size even though the array is declared as static 
 immutable.

     import std.traits;
     static immutable int[] a  = [1,2,3];
     static assert(isStaticArray!(typeof(a)));  // fails
I still like ---- auto s(T, size_t n)(T[n] arr) { return arr; } auto arr = [1, 2, 3].s; ---- But of course this won't work: ---- int[] a = [1,2,3].s; static assert(isStaticArray!(typeof(a))); // fails ---- Since 'a' is just a slice. But this will work: ---- immutable auto a = [1,2,3].s; ----
May 02 2016
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/2/16 1:43 PM, Namespace wrote:
 On Monday, 2 May 2016 at 13:00:27 UTC, Erik Smith wrote:
 Is there a way to initialize a static array and have it's size
 inferred (and that works for arrays of structs using braced
 literals)?  This would make it easier to maintain longer static array
 definitions.  The code below doesn't work when removing the array size
 even though the array is declared as static immutable.

     import std.traits;
     static immutable int[] a  = [1,2,3];
     static assert(isStaticArray!(typeof(a)));  // fails
I still like ---- auto s(T, size_t n)(T[n] arr) { return arr; }
Interesting. But there is a major problem here...
 auto arr = [1, 2, 3].s;
 ----

 But of course this won't work:
 ----
 int[] a  = [1,2,3].s;
 static assert(isStaticArray!(typeof(a)));  // fails
 ----
And this is the problem.
 Since 'a' is just a slice.
A slice of a no-longer-existing temporary! Admittedly, this is not an issue with your code, but a deeper issue of allowing slicing of rvalues.
 But this will work:
 ----
 immutable auto a  = [1,2,3].s;
 ----
You can drop auto. It's just a placeholder for the storage class in the case where a storage class isn't specified. -Steve
May 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
 A slice of a no-longer-existing temporary! Admittedly, this is 
 not an issue with your code, but a deeper issue of allowing 
 slicing of rvalues.
This works: ---- int[] as = [1, 2, 3].s; writeln(as[2]); ---- Bug or feature? Or did I may misunderstood you?
 You can drop auto. It's just a placeholder for the storage 
 class in the case where a storage class isn't specified.
Right, I forgot, it's a bit since I wrote something in D.
May 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 2 May 2016 at 18:57:49 UTC, Namespace wrote:
 A slice of a no-longer-existing temporary! Admittedly, this is 
 not an issue with your code, but a deeper issue of allowing 
 slicing of rvalues.
This works: ---- int[] as = [1, 2, 3].s; writeln(as[2]);
Of course this slice is only valid as long as you dont leave the scope. That's maybe what you tried to say, right?
May 02 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/2/16 3:02 PM, Namespace wrote:
 On Monday, 2 May 2016 at 18:57:49 UTC, Namespace wrote:
 A slice of a no-longer-existing temporary! Admittedly, this is not an
 issue with your code, but a deeper issue of allowing slicing of rvalues.
This works: ---- int[] as = [1, 2, 3].s; writeln(as[2]);
Of course this slice is only valid as long as you dont leave the scope. That's maybe what you tried to say, right?
No, because 'as' is pointing at stack space no longer allocated. It may work, and it may not, but having it work is not proof that it's sound. To make things clear, this is what your code effectively does: int[] as = void; { auto _tmp = [1, 2, 3].s; as = _tmp; } Which is not the same thing as having the static array a defined stack variable in the same scope. -Steve
May 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 2 May 2016 at 19:08:52 UTC, Steven Schveighoffer wrote:
 On 5/2/16 3:02 PM, Namespace wrote:
 On Monday, 2 May 2016 at 18:57:49 UTC, Namespace wrote:
 A slice of a no-longer-existing temporary! Admittedly, this 
 is not an
 issue with your code, but a deeper issue of allowing slicing 
 of rvalues.
This works: ---- int[] as = [1, 2, 3].s; writeln(as[2]);
Of course this slice is only valid as long as you dont leave the scope. That's maybe what you tried to say, right?
No, because 'as' is pointing at stack space no longer allocated. It may work, and it may not, but having it work is not proof that it's sound. To make things clear, this is what your code effectively does: int[] as = void; { auto _tmp = [1, 2, 3].s; as = _tmp; } Which is not the same thing as having the static array a defined stack variable in the same scope. -Steve
The assembler looks different than that but I may be wrong and I only looked at gdc. But anyway, that means with 'pragma(inline, true)' I'm save. Is that right?
May 02 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/2/16 3:12 PM, Namespace wrote:
 On Monday, 2 May 2016 at 19:08:52 UTC, Steven Schveighoffer wrote:
 On 5/2/16 3:02 PM, Namespace wrote:
 On Monday, 2 May 2016 at 18:57:49 UTC, Namespace wrote:
 A slice of a no-longer-existing temporary! Admittedly, this is not an
 issue with your code, but a deeper issue of allowing slicing of
 rvalues.
This works: ---- int[] as = [1, 2, 3].s; writeln(as[2]);
Of course this slice is only valid as long as you dont leave the scope. That's maybe what you tried to say, right?
No, because 'as' is pointing at stack space no longer allocated. It may work, and it may not, but having it work is not proof that it's sound. To make things clear, this is what your code effectively does: int[] as = void; { auto _tmp = [1, 2, 3].s; as = _tmp; } Which is not the same thing as having the static array a defined stack variable in the same scope.
The assembler looks different than that but I may be wrong and I only looked at gdc. But anyway, that means with 'pragma(inline, true)' I'm save. Is that right?
The assembler might be safe in some instances, but that doesn't reflect the original internal representation in the compiler. Some other configuration of calls may allow the compiler to reuse that memory, and then you run into problems. I'm wondering if you used my rewrite if it would actually output the same thing? But in any case, I don't know the answer to the pragma(inline) thing. I would guess it is subject to the same issues, but I'm not 100% sure. -Steve
May 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
 The assembler might be safe in some instances, but that doesn't 
 reflect the original internal representation in the compiler. 
 Some other configuration of calls may allow the compiler to 
 reuse that memory, and then you run into problems.

 I'm wondering if you used my rewrite if it would actually 
 output the same thing?
Not quite. Look for yourself: https://godbolt.org/g/kO8hdW https://godbolt.org/g/KCfYPy
May 02 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/2/16 3:38 PM, Namespace wrote:
 The assembler might be safe in some instances, but that doesn't
 reflect the original internal representation in the compiler. Some
 other configuration of calls may allow the compiler to reuse that
 memory, and then you run into problems.

 I'm wondering if you used my rewrite if it would actually output the
 same thing?
Not quite. Look for yourself: https://godbolt.org/g/kO8hdW https://godbolt.org/g/KCfYPy
Except for offsets, it looks identical. May be the compiler puts things in different spots for different ways of writing. But the thing that's not disclosed here is: what happens when the compiler feels the need to reuse that stack space? Your example doesn't have anything that may compete for the space, it just returns after this is over. For example, define a static array of exactly the same size to use after the call: int[3] x; Now, see if x is pigeonholed into that same place your temporary return existed (and therefore 'as' points at it). I'm still fully on the side of this being unsafe. -Steve
May 02 2016
parent reply Namespace <rswhite4 gmail.com> writes:
On Monday, 2 May 2016 at 20:05:15 UTC, Steven Schveighoffer wrote:
 On 5/2/16 3:38 PM, Namespace wrote:
 The assembler might be safe in some instances, but that 
 doesn't
 reflect the original internal representation in the compiler. 
 Some
 other configuration of calls may allow the compiler to reuse 
 that
 memory, and then you run into problems.

 I'm wondering if you used my rewrite if it would actually 
 output the
 same thing?
Not quite. Look for yourself: https://godbolt.org/g/kO8hdW https://godbolt.org/g/KCfYPy
Except for offsets, it looks identical. May be the compiler puts things in different spots for different ways of writing. But the thing that's not disclosed here is: what happens when the compiler feels the need to reuse that stack space? Your example doesn't have anything that may compete for the space, it just returns after this is over. For example, define a static array of exactly the same size to use after the call: int[3] x; Now, see if x is pigeonholed into that same place your temporary return existed (and therefore 'as' points at it). I'm still fully on the side of this being unsafe. -Steve
I used it very often, but always assigned the result to an auto-type variable, never to a slice.
May 02 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/2/16 4:14 PM, Namespace wrote:

 I used it very often, but always assigned the result to an auto-type
 variable, never to a slice.
Using auto is fine, but the slice should not happen if you are assigning slice to a local var. There is a bug report on this: https://issues.dlang.org/show_bug.cgi?id=12625, though it's not as focused as I think it should be. -Steve
May 02 2016
prev sibling next sibling parent Basile B. <b2.temp gmx.com> writes:
On Monday, 2 May 2016 at 17:43:56 UTC, Namespace wrote:
 I still like
 ----
 auto s(T, size_t n)(T[n] arr) {
 	return arr;
 }
Wooah, this trick is awesome. But actually it does the same thing that what I've proposed before. Exactly the same code is generated. So I'd say that it's rather a matter of readability. https://godbolt.org/g/wGTs52 https://godbolt.org/g/cXECyO
May 03 2016
prev sibling parent reply =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Monday, 2 May 2016 at 17:43:56 UTC, Namespace wrote:
 immutable auto a  = [1,2,3].s;
Will that have zero run-time overhead compared to: immutable int[3] a = [1,2,3]; ?
Oct 18 2016
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/18/16 6:35 AM, Nordlöw wrote:
 On Monday, 2 May 2016 at 17:43:56 UTC, Namespace wrote:
 immutable auto a  = [1,2,3].s;
Will that have zero run-time overhead compared to: immutable int[3] a = [1,2,3];
Neither will have zero runtime overhead, but use the disassembler to see if there is a difference. My understanding is that while the compiler used to allocate whenever it saw an array literal (including your above usage), that is no longer the case. -Steve
Oct 18 2016
prev sibling parent Namespace <rswhite4 gmail.com> writes:
On Tuesday, 18 October 2016 at 10:35:44 UTC, Nordlöw wrote:
 On Monday, 2 May 2016 at 17:43:56 UTC, Namespace wrote:
 immutable auto a  = [1,2,3].s;
Will that have zero run-time overhead compared to: immutable int[3] a = [1,2,3]; ?
I'm not quite sure if pragma(inline, true) would result in zero runtime overhead, but without you have 3 lines of assembler more (with gdc). https://godbolt.org/g/JUjP1d https://godbolt.org/g/qaqylp
Oct 18 2016