www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Array of structs construction

reply "bearophile" <bearophileHUGS lycos.com> writes:
I'd like to write code like this (the constructors of the structs 
must take 1 argument):

// some imports here
void main() {
     BigInt[] data1 = [5, 6, 9];
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     Nibble[] data3 = [1, 2, 15]; // Nibble.sizeof == 1
     alias Typedef!int Mint;
     Mint[] data4 = [5, 6, 9];
}


Do you like this feature?


Scala accepts a similar syntax:
http://ideone.com/mFuVxP


// Scala code
object Main extends App {
   val data : Array[BigInt] = Array(10, 20, 30)
}


Currently in D you write this, it's not handy if you have many 
items:

// some imports here
void main() {
     auto data1 = [BigInt(5), BigInt(6), BigInt(9)];
     alias Ranged!(int,5,10) R; // a short name
     auto data2 = [R(5), R(6), R(9)];
     auto data3 = [Nibble(1), Nibble(2), Nibble(15)];
     alias Typedef!int Mint;
     Mint[] data4 = [Mint(5), Mint(6), Mint(9)];
}


Or you duplicate the arrays to avoid the bit liberals:

import std.bigint;
void main() {
     auto aux = [5, 6, 9];
     auto data1 = new BigInt[aux.length];
     foreach (i, a; aux)
         data1[i] = BigInt(a);
     // ...
}

Bye,
bearophile
Oct 19 2012
next sibling parent "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Saturday, 20 October 2012 at 01:06:57 UTC, bearophile wrote:
 I'd like to write code like this (the constructors of the 
 structs must take 1 argument):

 // some imports here
 void main() {
     BigInt[] data1 = [5, 6, 9];
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     Nibble[] data3 = [1, 2, 15]; // Nibble.sizeof == 1
     alias Typedef!int Mint;
     Mint[] data4 = [5, 6, 9];
 }


 Do you like this feature?

 Currently in D you write this, it's not handy if you have many 
 items:

 // some imports here
 void main() {
     auto data1 = [BigInt(5), BigInt(6), BigInt(9)];
     alias Ranged!(int,5,10) R; // a short name
     auto data2 = [R(5), R(6), R(9)];
     auto data3 = [Nibble(1), Nibble(2), Nibble(15)];
     alias Typedef!int Mint;
     Mint[] data4 = [Mint(5), Mint(6), Mint(9)];
 }

Only way i can see this working simply, is if it's auto, at least one of the items have to specify it's full type and the rest can be ignored. Also it must have a constructor that takes that one type of argument. So.. //50 & 100 aren't BigInt, but can be constructed to be one.. auto bignums = [BigInt(25), 50, 100]; As for above arrays where the type is known, I can't see a reason why it can't work. In a large array of static data I've had to make shortcut names for several structs in order to keep it readable and programmable. This may help in some cases, or perhaps all of them. Course this also means if a structure is being initialized then perhaps it can be extended. Like the above, except... struct S { int x,y; BigInt z; } S s = {1,2,3}; //3 converted if BigInt has a constructor for int //if that's illegal, then you'd do this to make it work instead, //which seems... unnecessary. struct S { int x,y; BigInt[1] z; } S s = {1,2,[3]}; The downside is if there's memory allocation or heavy work that can't be done at compile-time then there may be more complex issues trying to optimize something. Makes it sorta mixed.
Oct 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Oct 20, 2012 at 03:06:56AM +0200, bearophile wrote:
 I'd like to write code like this (the constructors of the structs
 must take 1 argument):
 
 // some imports here
 void main() {
     BigInt[] data1 = [5, 6, 9];
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     Nibble[] data3 = [1, 2, 15]; // Nibble.sizeof == 1
     alias Typedef!int Mint;
     Mint[] data4 = [5, 6, 9];
 }
 
 
 Do you like this feature?

What about mixin() with a CTFE function that expands an array of initializers at compile-time? Something like: string structArray(S,T...)(T args) if (is(typeof(S(args[0])))) { // Warning: untested code return "[" ~ join( map!(a) => S.stringof ~ "(" ~ a ~ ")", ",") ~ "]"; } S[] sArr = mixin(structArray(elem0, elem1, elem2)); T -- English has the lovely word "defenestrate", meaning "to execute by throwing someone out a window", or more recently "to remove Windows from a computer and replace it with something useful". :-) -- John Cowan
Oct 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Oct 19, 2012 at 10:32:48PM -0700, H. S. Teoh wrote:
 On Sat, Oct 20, 2012 at 03:06:56AM +0200, bearophile wrote:
 I'd like to write code like this (the constructors of the structs
 must take 1 argument):
 
 // some imports here
 void main() {
     BigInt[] data1 = [5, 6, 9];
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     Nibble[] data3 = [1, 2, 15]; // Nibble.sizeof == 1
     alias Typedef!int Mint;
     Mint[] data4 = [5, 6, 9];
 }
 
 
 Do you like this feature?

What about mixin() with a CTFE function that expands an array of initializers at compile-time? Something like:

OK, here's a tested, working example: import std.stdio; string structArray(S)(string[] args...) { string s = "["; string delim = ""; foreach (a; args) { s ~= delim ~ S.stringof ~ "(" ~ a ~ ")"; delim = ","; } return s ~ "]"; } struct AStruct { int x, y; } void main() { AStruct[] arr = mixin(structArray!AStruct("1,1", "1,2", "2,2", "2,3")); writeln(arr); } T -- "Life is all a great joke, but only the brave ever get the point." -- Kenneth Rexroth
Oct 19 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Oct 19, 2012 at 10:42:08PM -0700, H. S. Teoh wrote:
[...]
 	import std.stdio;
 	string structArray(S)(string[] args...) {
 		string s = "[";
 		string delim = "";
 		foreach (a; args) {
 			s ~= delim ~ S.stringof ~ "(" ~ a ~ ")";
 			delim = ",";
 		}
 		return s ~ "]";
 	}

Here's how to use it for making BigInt arrays: void main() { BigInt[] arr = mixin(structArray!BigInt( `"12345678901234567890"`, `"4294967296000"`, `"1000000000000000000000000000000000"`, `"1203004000500006000007000000800000009"`, )); writeln(arr); } It works for any kind of struct with ctors. Should be trivially extendible to array of class objects. Some improvement in usage syntax is probably possible, too, but I'll leave it up to the CTFE experts here to figure that out. ;-) T -- Why ask rhetorical questions? -- JC
Oct 19 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
H. S. Teoh:

 It works for any kind of struct with ctors. Should be trivially
 extendible to array of class objects. Some improvement in usage 
 syntax is probably possible, too, but I'll leave it up to the
 CTFE experts here to figure that out. ;-)

I have 2D array literals. Thank you for your work. It shows how far you go with library code. Bye, bearophile
Oct 21 2012
prev sibling next sibling parent "timotheecour" <thelastmammoth gmail.com> writes:
Can't we simply use map, so as not to introduce new syntax?

import std.algorithm:map;
import std.array:array;

// some imports here
void main() {
     //BigInt[] data1 = [5, 6, 9];
     auto data1 = [5, 6, 9].map!(a=>BigInt(a)).array;
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     auto data2 = [5, 6, 9].map!(a=>Ranged!(int,5,10)).array;
}

I think this is less verbose than something like:
AStruct[] arr = mixin(structArray!AStruct("1,1", "1,2", "2,2", 
"2,3"));

Also, we can make it even more concise by:
auto data1 = [5, 6, 9].map!(make!BigInt).array; //make has been 
proposed as a proxy to construct a struct/class

or even the very concise:
auto data1 = [5, 6, 9].mapMake!BigInt;

where mapMake would be similar to std.algorithm.map except it 
would take a struct (or class) type instead of a delegate. It's 
code should be fairly obvious. We could choose whether to return 
a lazy range or not in that case so the ".array" is not required.
Oct 22 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
timotheecour:

 Can't we simply use map, so as not to introduce new syntax?

This works today: import std.algorithm, std.array, std.bigint; void main() { auto data1 = [5, 6, 9].map!BigInt().array(); } But literals like: Ranged!(int,2,20)[] data2 = [5, 6, 9]; Are useful to make the code stronger, because the invariant of that Ranged is verified statically, despite data2 is not an enum. Right? Bye, bearophile
Oct 23 2012
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 23 October 2012 at 04:51:01 UTC, timotheecour wrote:
 void main() {
     //BigInt[] data1 = [5, 6, 9];
     auto data1 = [5, 6, 9].map!(a=>BigInt(a)).array;
     Ranged!(int,5,10)[] data2 = [5, 6, 9];
     auto data2 = [5, 6, 9].map!(a=>Ranged!(int,5,10)).array;
 }

Syntax construct a=>b causes nasty bug covered up by map template. It is related to 8854, 8832 and 7978 and several topics at forum associated with these issues (strange behavior using map together with delegates). The problem is that typeless undeclared identifier "a" causes syntax construct a=>b to be of type void rather than delegate. Once syntax is fixed, type of expression becomes as expected http://dpaste.dzfl.pl/738f7301 Sometimes this leads to observable incorrect behavior, sometimes not. For example, if you have -----main.d-------------- version (bug) { import std.string; } import test; void main() { testfun(); } -------test.d----------------- import std.algorithm; import std.stdio; void testfun() { int c = 0; writeln([0].map!(a=>c)[0]); } ----------------------------- then the behavior of program would depend on whether bug version is set or not.
Oct 23 2012