How to use 'Last attribute on multidimensional arrays in Ada?

Go To StackoverFlow.com

1

I am trying to use the 'Last attribute with a 2D array in Ada, but I can't seem to find the correct syntax to do so.

I know that if I have a 1D array/vector I can use A'last or A'last(n) where n is the nth dimension. But if I do the following

type UnconstrainedArray_2D is array (Integer range <>, Integer range <>) of Integer;

function temp(tempIn : in Integer;
              Table  : in UnconstrainedArray_2D) return Integer is
tempTable : UnconstrainedArray_2D(0..tempIn, 0..tempIn);

begin
    for i in 0..tempTable'last(1) loop
        for j in 0..tempTable'last(2) loop
            tempTable(i, j) := Table(i,j);
        end loop;
    end loop;
end temp;

I get the following compile time error:

Storage_Error stack overflow (or erroneous memory access)

So what am I doing wrong?

I am using GNAT Pro 6.4.1 on Linux.

2012-04-04 19:40
by Fred
I have found that the error is related to the Table : in UnconstrainedArray_2D in the function. But I'm still not sure what I have done wrong - Fred 2012-04-04 20:21
I have also updated to GNAT Pro 7.0.1 to see if it was a compiler bug, but I get the same error - Fred 2012-04-04 20:23
Not a valid excerpt, I assume "temptable is UnconstrainedArray2D..." s.b. "temptable : UnconstrainedArray2D...". Fixing that, the other key missing data is the arguments you provide in the call to temp(). If they're large values, allocating a local variable (tempTable) could easily blow the stack. There's also brittle assumptions being made about array index ranges that require the tempIn and Table arguments be consistent with the declaration of the tempTable variable--e.g. Table assumed to start with 0 indices, tempIn assumed to be >= than the Table indices. 'Last is not your problem - Marc C 2012-04-04 20:44
I agree that 'Last is not the problem. I am beginning to see that it is the unconstrained array - Fred 2012-04-04 21:06
If you are using GNAT Pro, your employer is paying AdaCore a substantial subscription to have them answer this kind of question for you! Whoever you ask, though, they will find it much easier to help you if you provide properly compilable code - Simon Wright 2012-04-04 21:56
@Simon Yes you are correct. And they do answer... after 2 or 3 days - Fred 2012-04-04 22:22


3

I'd be very surprised if you got a compile-time Storage_Error on that code.

I've grabbed a copy of your code and modified it as follows; it compiles without error using GNAT (gcc-4.4):

procedure Array_2D is
    type UnconstrainedArray_2D is array (Integer range <>, Integer range <>) of Integer;

    function temp(tempIn : in Integer;
                  Table  : in UnconstrainedArray_2D) return Integer is
    tempTable : UnconstrainedArray_2D(0..tempIn, 0..tempIn);

    begin
        for i in 0..tempTable'last(1) loop
            for j in 0..tempTable'last(2) loop
                tempTable(i, j) := Table(i,j);
            end loop;
        end loop;
        return 42; -- added this
    end temp;
begin
    null;
end Array_2D;

(Note that I had to add the missing return statement in temp.)

Your syntax for the 'Last attribute (not "command") is correct, but since Ada arrays can have arbitrary lower and upper bounds, it's better to use the 'Range attribute instead:

    for i in tempTable'Range(1) loop
        for j in tempTable'Range(2) loop
            tempTable(i, j) := Table(i,j);
        end loop;
    end loop;

As for the Storage_Error exception, that could easily happen at run time (not compile time) if you call your temp function with a very large value for tempIn. Remember that it has to allocate enough space to hold tempIn**2 Integer objects. Presumably you've also created another UnconstrainedArray_2D object to be passed in as the Table parameter.

It's conceivable that the compiler itself could die with a Storage_Error exception, but I don't see anything in your code that might cause that.

Show us a complete (but small) program that demonstrates the problem you're having, along with the exact (copy-and-pasted) error message. Please distinguish clearly between compile-time and run-time errors.

2012-04-04 21:44
by Keith Thompson
Sorry, that I didn't post compilable code. I had thought that my error was syntatic one and posted the code that would provide the ability to identify the incorrect syntax.

Thank you for the example. The error I get is definately at compile time and I have found that if I change type UnconstrainedArray_2D is array (Integer range <>, Integer range <>) of Integer; to type UnconstrainedArray_2D is array (Integer range 0..Integer'last, Integer range 0..Integer'last) of Integer; then the Storage_Error disappers - Fred 2012-04-04 22:29

Did you really get a compile-time error - Keith Thompson 2012-04-04 22:31
Yes, it states compilation abandoned gnatmake: "file.adb" compilation errorFred 2012-04-04 22:34
@Fred: I don't see Storage_Error in that message. There should be a message from the compiler itself, referring to a specific line in file.adb - Keith Thompson 2012-04-04 22:56
@Fred: You expressed concern that support from GNAT Pro would take too long. If you want answers, you really should answer our questions. You still haven't shown us either the exact error message or your actual code - Keith Thompson 2012-04-05 18:20


2

Your tempTable might have a range of 0..tempIn, but you don't know what range your Table has.. They could be of different length, too.

You would have to check that the length is the same and then use relative indices, like this:

function temp(tempIn : in Integer;
              Table  : in UnconstrainedArray_2D) return Integer is
    tempTable : UnconstrainedArray_2D(0..tempIn, 0..tempIn);

begin
    if tempTable'Length (1) /= Table'Length (1) or else
      tempTable'Length (2) /= Table'Length (2)
    then
       raise Constraint_Error; -- or something else
    end if;
    for i in 0 .. tempTable'Length (1) - 1 loop
        for j in 0 .. tempTable'Length (2) - 1 loop
            tempTable(tempTable'First (1) + i, tempTable'First (2) + j) :=
              Table(Table'First (1) + i, Table'First (2) + j);
        end loop;
    end loop;
end temp;

that way it is ensured that both tables are same length and all indices are valid.

If your tempTable is allowed to be smaller than Table, simply adjust the length check to >. The indices would still be valid.

2012-04-05 06:33
by oenone


0

I don't see an actual value for tempIn set. If the value for tempIn coming into the function temp has not been properly initialized or explicitly set, then the value in tempIn could be anything and probably not something you would like.


I was thinking of a default value. (probably shouldn't post when I am not feeling well :-)

2012-04-06 14:17
by Eryndlia Mavourneen
Why would you expect to see an actual value for tempIn? This is a function declaration, not a function call - Simon Wright 2012-04-06 21:57
Ads