www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Outside array bounds

reply vino.B <bheeman.vino hotmail.com> writes:
Hi All,

   Request you help, on the below code

import std.stdio: writeln;

void process(T ...)(string ID, T args) {
if (ID == "I1") { writeln(args.length, "\t", args[0]); }
else if (ID == "I2") { writeln(args.length, "\t", args[1]);}
}

void main() {
string S1 = "Test1", S2 = "Test2", ID1 = "I1", ID2 = "I2";
int Size = 1;
process(ID1, S1);
process(ID2, S2, Size);
}

Error:
Test.d(5): Error: array index [1] is outside array bounds [0 .. 1]
Test.d(11): Error: template instance `Test.process!string` error 
instantiating

From,
Vino.B
Jul 07 2018
next sibling parent Flaze07 <christianseiji.cs gmail.com> writes:
On Saturday, 7 July 2018 at 08:09:51 UTC, vino.B wrote:
 Hi All,

   Request you help, on the below code

 import std.stdio: writeln;

 void process(T ...)(string ID, T args) {
 if (ID == "I1") { writeln(args.length, "\t", args[0]); }
 else if (ID == "I2") { writeln(args.length, "\t", args[1]);}
 }

 void main() {
 string S1 = "Test1", S2 = "Test2", ID1 = "I1", ID2 = "I2";
 int Size = 1;
 process(ID1, S1);
 process(ID2, S2, Size);
 }

 Error:
 Test.d(5): Error: array index [1] is outside array bounds [0 .. 
 1]
 Test.d(11): Error: template instance `Test.process!string` 
 error instantiating

 From,
 Vino.B
in the first process template instantiation, you have a code that has args[ 1 ], despite the size being 1, which means you can only have args[ 0 ], how about changing the code to instead args[ $ - 1 ] ?
Jul 07 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Saturday, 7 July 2018 at 08:09:51 UTC, vino.B wrote:
 Hi All,

   Request you help, on the below code

 import std.stdio: writeln;

 void process(T ...)(string ID, T args) {
 if (ID == "I1") { writeln(args.length, "\t", args[0]); }
 else if (ID == "I2") { writeln(args.length, "\t", args[1]);}
 }

 void main() {
 string S1 = "Test1", S2 = "Test2", ID1 = "I1", ID2 = "I2";
 int Size = 1;
 process(ID1, S1);
 process(ID2, S2, Size);
 }

 Error:
 Test.d(5): Error: array index [1] is outside array bounds [0 .. 
 1]
 Test.d(11): Error: template instance `Test.process!string` 
 error instantiating

 From,
 Vino.B
Interesting.. Looks like the compiler does some boundschecking during compile time. You could circumvent this: void process(T ...)(string ID, T args) { if (ID == "I1") { writeln(args.length, "\t", args[0]); } static if (T.length > 1) // only add below code if cond. true if (ID == "I2") { writeln(args.length, "\t", args[1]);} } Oddly, when leaving out the "static if" statement, the error still occurs when compiling with "-release" or with "-boundscheck=off"... I thought those would/should disable bounds checking at compile time???
Jul 07 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 7 July 2018 at 08:24:21 UTC, Timoses wrote:
 Interesting.. Looks like the compiler does some boundschecking 
 during compile time. You could circumvent this:


     void process(T ...)(string ID, T args) {
         if (ID == "I1") { writeln(args.length, "\t", args[0]); }
         static if (T.length > 1) // only add below code if 
 cond. true
             if (ID == "I2") { writeln(args.length, "\t", 
 args[1]);}
     }

 Oddly, when leaving out the "static if" statement, the error 
 still occurs when compiling with "-release" or with 
 "-boundscheck=off"... I thought those would/should disable 
 bounds checking at compile time???
Nonono )) In this case, the bound checks are like interface checking... or like type checking... As there are different processes instantiations for different number of args. I hope this can't be turned off so easily... ))))
Jul 07 2018
parent reply Timoses <timosesu gmail.com> writes:
On Saturday, 7 July 2018 at 08:35:27 UTC, Alex wrote:
 On Saturday, 7 July 2018 at 08:24:21 UTC, Timoses wrote:
 Interesting.. Looks like the compiler does some boundschecking 
 during compile time. You could circumvent this:


     void process(T ...)(string ID, T args) {
         if (ID == "I1") { writeln(args.length, "\t", args[0]); 
 }
         static if (T.length > 1) // only add below code if 
 cond. true
             if (ID == "I2") { writeln(args.length, "\t", 
 args[1]);}
     }

 Oddly, when leaving out the "static if" statement, the error 
 still occurs when compiling with "-release" or with 
 "-boundscheck=off"... I thought those would/should disable 
 bounds checking at compile time???
Nonono )) In this case, the bound checks are like interface checking... or like type checking... As there are different processes instantiations for different number of args. I hope this can't be turned off so easily... ))))
Aw, got it. So args is actually a tuple type where accessing beyond the defined tuple (T) is invalid? auto a = [1, 2, 4]; // works pragma(msg, typeof(a[3])); auto t = tuple(3, 4, 5.3); // ERROR: // pragma(msg, typeof(t[3]));
Jul 07 2018
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 7 July 2018 at 11:22:38 UTC, Timoses wrote:
 Aw, got it. So args is actually a tuple type where accessing 
 beyond the defined tuple (T) is invalid?

       auto a = [1, 2, 4];
       // works
       pragma(msg, typeof(a[3]));

       auto t = tuple(3, 4, 5.3);
       // ERROR:
       // pragma(msg, typeof(t[3]));
Yes. The absence of the next element in the array, doesn't matter, while asking its type, while the absence of the next tuple element means also the absence of any type, as I understand it.
Jul 07 2018
parent reply vino.B <bheeman.vino hotmail.com> writes:
On Saturday, 7 July 2018 at 12:13:21 UTC, Alex wrote:
 On Saturday, 7 July 2018 at 11:22:38 UTC, Timoses wrote:
 Aw, got it. So args is actually a tuple type where accessing 
 beyond the defined tuple (T) is invalid?

       auto a = [1, 2, 4];
       // works
       pragma(msg, typeof(a[3]));

       auto t = tuple(3, 4, 5.3);
       // ERROR:
       // pragma(msg, typeof(t[3]));
Yes. The absence of the next element in the array, doesn't matter, while asking its type, while the absence of the next tuple element means also the absence of any type, as I understand it.
Hi All, If we replace the statement as args[$ -1] the program works are expected, if we apply the same logic in different approach it does not work, in the below code if we command the first block "if (fnID == "ListFilesNames") {} " then the second "if (fnID == "ListFilesSize") {}" works fine, and vice versa but if both the "if" block is un-commented it does not work , rather it passes wrong parameter to the function. Tried change the args as args[$] , args[1], args[2], Error: Test.d(31): Error: function Test.ListFilesSize(string FFs, int Size) is not callable using argument types (string, Array!string) VarTest.d(31): cannot pass argument _param_1 of type Array!string to parameter int Size Test.d(42): Error: template instance `Test.process!(Array!string)` error instantiating Code: import std.stdio: writeln; import std.container.array; import std.algorithm: filter, map; import std.typecons: Tuple, tuple; import std.file: dirEntries, isFile, SpanMode; import std.conv: to; import std.datetime.systime: Clock, SysTime; import std.parallelism: parallel, taskPool, TaskPool; import std.array: empty; auto ListFilesNames (string FFs) { auto dFiles = Array!(Tuple!(string, SysTime))(dirEntries(FFs, SpanMode.shallow).filter!(a => a.isFile).map!(a => tuple(a.name, a.timeCreated))); return dFiles; } auto ListFilesSize(string FFs, int Size) { writeln(Size); auto dFiles = Array!(Tuple!(string, ulong))(dirEntries(FFs, SpanMode.shallow).filter!(a => a.isFile).map!(a => tuple(a.name, (a.size > Size).to!ulong))); return dFiles; } void process(T ...)(string fnID, T args) { if (fnID == "ListFilesNames") { alias scRType = typeof(ListFilesNames(string.init)); auto PFresult = taskPool.workerLocalStorage!scRType(); foreach (string FFs; args[0]) { PFresult.get ~= ListFilesNames(FFs); } foreach(i; PFresult.toRange) { writeln(!i[][].empty); } } else if (fnID == "ListFilesSize") { alias scRType = typeof(ListFilesSize(string.init, ulong.init)); auto PFresult = taskPool.workerLocalStorage!scRType(); foreach (string FFs; args[0]) { PFresult.get ~= ListFilesSize(FFs, args[$ - 1]); } foreach(i; PFresult.toRange) { writeln(!i[][].empty); } } } void main() { Array!string NameDir, SizeDir; NameDir.insert("C:\\Temp\\BACKUP1"); SizeDir.insert("C:\\Temp\\TEAM1"); int Size = 1; string fnID1 = "ListFilesNames", fnID2 = "ListFilesSize"; process(fnID1, NameDir); process(fnID2, SizeDir, Size); } From, Vino.B
Jul 07 2018
parent Timoses <timosesu gmail.com> writes:
On Saturday, 7 July 2018 at 15:25:51 UTC, vino.B wrote:
 Hi All,

   If we replace the statement as args[$ -1] the program works 
 are expected, if we apply the same logic in different approach 
 it does not work, in the below code if we command the first 
 block "if (fnID == "ListFilesNames") {} " then the second "if 
 (fnID == "ListFilesSize") {}" works fine, and vice versa but if 
 both the "if" block is un-commented it does not work , rather 
 it passes wrong parameter to the function. Tried change the 
 args as args[$] , args[1], args[2],

 Error:
 Test.d(31): Error: function Test.ListFilesSize(string FFs, int 
 Size) is not callable using argument types (string, 
 Array!string)
 VarTest.d(31):        cannot pass argument _param_1 of type 
 Array!string to parameter int Size
 Test.d(42): Error: template instance 
 `Test.process!(Array!string)` error instantiating


 Code:
[...]
 void process(T ...)(string fnID, T args) {
Throw in a pragma here: void process(T ...)(string fnID, T args) { pragma(msg, T); ... } [...]
 void main() {
 Array!string NameDir, SizeDir;
 NameDir.insert("C:\\Temp\\BACKUP1");
 SizeDir.insert("C:\\Temp\\TEAM1");
 int Size = 1;
 string fnID1 = "ListFilesNames", fnID2 = "ListFilesSize";
 process(fnID1, NameDir); // 1
 process(fnID2, SizeDir, Size); // 2
If you call '// 1' first, then the output would be (Array!string) ... ERROR and switch the order: process(fnID2, SizeDir, Size); // 2 process(fnID1, NameDir); // 1 Now you'll see from the pragma output: (Array!string, int) (Array!string) ... ERROR So the '// 2' call actually compiled fine. The compiler complains because the function would try to call ListFilesSize(int) with the parameter args[$-1], which in this case would be the last element, so Array!string. `args` is a compile time-construct. In effect you instantiate two templates with the following calls: process(fnID1, NameDir); // same as process!(Array!string)(fnID1, NameDir) instantiates the function void process(string fnID, (Array!string) args) { ... // accessing args[$-1] equals accessing args[0] (only one element) ListFilesSize(FFs, args[$ - 1]); // above function will call ListFilesSize(string, Array!string).. which does not exist, hence the error } and process(fnID2, SizeDir, Size); // same as process!(Array!string, int)(fnID1, NameDir, Size) instantiates the function void process(string fnID, (Array!string, int) args) { ... // this time args.length == 2 // hence the call to args[$-1] == args[1] == int ListFilesSize(FFs, args[$-1]); // compiles!!! args[$-1] fits ListFilesSize signature! It helps me to think in two domains. There is the "static" CT (compile-time) and the RT (run-time) domain. You can vary how CT code is laid out with "static" statements (such as "static if" or "static foreach", or use aliases. Beware that "static this()" or static functions are something different once again). So you would have to determine _at compile time_ how the 'process' function should look given it's arguments: void process(T ...)(T args) { static if (T.length == 1 && is(T[0] == Array!string)) { // 1 } else static if (T.length == 2 && is(T[0] == Array!string) && is(T[1] == int)) { // 2 } } So in case above function get's called with only one argument which is an Array!string, only the '// 1' part is left for the run-time to be executed. OR you could test if the function compiles void process(T ...)(T args) { static if (is(typeof(ListFilesSize(args[$-1])))) ListFilesSize(args[$-1]); else static assert(false); } Another option would be to use template constraints void process(T ...)(T args) if (is(typeof(ListFilesSize(args[$-1])))) { ListFilesSize(args[$-1]); } This is a nice chapter to read (in fact the whole book is a great read): http://ddili.org/ders/d.en/cond_comp.html
Jul 07 2018
prev sibling parent Alex <sascha.orlov gmail.com> writes:
On Saturday, 7 July 2018 at 08:09:51 UTC, vino.B wrote:
 Hi All,

   Request you help, on the below code

 import std.stdio: writeln;

 void process(T ...)(string ID, T args) {
 if (ID == "I1") { writeln(args.length, "\t", args[0]); }
 else if (ID == "I2") { writeln(args.length, "\t", args[1]);}
 }

 void main() {
 string S1 = "Test1", S2 = "Test2", ID1 = "I1", ID2 = "I2";
 int Size = 1;
 process(ID1, S1);
 process(ID2, S2, Size);
 }

 Error:
 Test.d(5): Error: array index [1] is outside array bounds [0 .. 
 1]
 Test.d(11): Error: template instance `Test.process!string` 
 error instantiating

 From,
 Vino.B
Ok, the problem here is, that as args (and especially its length) are known at compile time, args are checked for having enough length in all cases of the run time parameter. My question would be, why do you need the ID string, if you know what to print from the length of args? ´´´ void process2(T ...)(string ID, T args) { static if(args.length == 1) { if (ID == "I1") { writeln(args.length, "\t", args[0]); } } else static if(args.length == 2) { if (ID == "I2") { writeln(args.length, "\t", args[1]); } } } void main() { string S1 = "Test1", S2 = "Test2", ID1 = "I1", ID2 = "I2"; int Size = 1; process2(ID1, S1); process2(ID2, S2, Size); } ´´´
Jul 07 2018