Specifying major order for packed matrix

See this declaration for 8 bits arranged in a matrix of 4x2, indexed by (row, column):

type Cell_Matrix is array (1..4, 1..2) of Boolean with
     Component_Size => 1,
     Size           => 8,
     Convention     => Fortran;

I was hoping that this would give column-major order but Unchecked_Conversion reveals row-major order.

After a few searches and ARM digging I could not find an answer to the following:

  1. Can a matrix major order be specified somehow? [Couldn’t find a way]
  2. If not, are Ada matrices guaranteed to be row-major? [I think not]

I have several workarounds at hand, no need to suggest alternatives. I’m interested on the normative behind it only. Thanks!

Hmm, considering this:

with Ada.Text_IO;         use Ada.Text_IO;
with Interfaces.Fortran;  use Interfaces.Fortran;

procedure Test is
   type Cell_Matrix is array (1..4, 1..2) of Real with
      Convention => Fortran;

   type Matrix is array (1..2, 1..4) of Real;

   A : Cell_Matrix := ((1.1, 1.2), (2.1, 2.2), (3.1, 3.2), (4.1, 4.2));
   B : Matrix;
   pragma Import (Ada, B);
   for B'Address use A'Address;
begin
   for I in A'Range (1) loop
      for J in A'Range (2) loop
         Put (Real'Image (A (I, J)));
         Put (' ');
      end loop;
      New_Line;
   end loop;

   New_Line;
   for I in B'Range (1) loop
      for J in B'Range (2) loop
         Put (Real'Image (B (I, J)));
         Put (' ');
      end loop;
      New_Line;
   end loop;
end Test;

The output is:

 1.10000E+00  1.20000E+00 
 2.10000E+00  2.20000E+00 
 3.10000E+00  3.20000E+00 
 4.10000E+00  4.20000E+00 

 1.10000E+00  2.10000E+00  3.10000E+00  4.10000E+00 
 1.20000E+00  2.20000E+00  3.20000E+00  4.20000E+00

It looks correct to me.

It is only Implementation Advice that arrays with Convention Fortran use column-major order, so a compiler does not have to do so. However, I know that GNAT does so, at least for unpacked arrays of numbers. You’re packing your array into a single byte, and that may conflict with or take precedence over the convention. I tried

with Ada.Text_IO;
with Ada.Unchecked_Conversion;

procedure Fortran_Test is
   type Cell_Base is array (1 .. 4, 1 .. 2) of Boolean;
   type Cell_Fortran is new Cell_Base with Convention => Fortran;
   type Cell_Packed  is new Cell_Base with Component_Size => 1, Size => 8;
   type Cell_PF      is new Cell_Packed with Convention => Fortran;
   
   B  : constant Cell_Base    := ( (False, False), (False, True), (True, False), (True, True) );
   F  : constant Cell_Fortran := Cell_Fortran (B);
   PB : constant Cell_Packed  := Cell_Packed (B);
   PF : constant Cell_PF      := Cell_PF (F);
   
   type Byte is mod 256;
   
   function To_Byte is new Ada.Unchecked_Conversion (Source => Cell_Packed, Target => Byte);
   function To_Byte is new Ada.Unchecked_Conversion (Source => Cell_PF, Target => Byte);
begin -- Fortran_Test
   Ada.Text_IO.Put_Line (Item => "PB =" & To_Byte (PB)'Image & " PF =" & To_Byte (PF)'Image);
end Fortran_Test;

with GNAT and got 216 for both. With ObjectAda I get PB = 216 PF = 2; the value for PF is clearly wrong.

So it’s the packing that in the case of GNAT is nullifying the Fortran convention.

Anyway, as long as those are recommendations and there’s no way to be sure that the compiler either gives you what you want or a compilation error, I’m not happy with relying on any behavior. And GNAT at least is not warning about not honoring the convention.

Thank you both for your tests.

It might be worth putting in a report with AdaCore and see if it is intended. Even though it is implementation advice, I always took that to be more of the advice was “to either provide the convention or not and if you do here are the rules”. It would be odd to have the convention only it doesn’t actually map to the language types correctly in some situations (without warnings).

This goes beyond Ada.

Fortran does have booleans and arrays of booleans. HOWEVER, it does not have bit arrays (packed arrays of booleans). Bit arrays in Fortran, as expected by the standard and by its users, are Arrays of integers on which bit operators can be performed (or just a single INT*8/16/…).

Therefore, what you are trying to do here is illegal in Fortran and its behaviour is either undefined or implementation defined. I think this is good. Though true, GNAT could issue a warning!

Also, I would like to see more Fortran/Ada interop ^^

1 Like

This is not a very satisfactory answer: the notion of an array of integers —even constrained to the range 0 to 1, inclusive— indexed in row- or column-major ordering is separate and distinct from the mere implementation-detail of how much space said integers take up.

+1 for more Fortran and Ada interop

Yes, I agree, but the problem remains. I also did not want to focus too much on the space required for the integers… Though I understand that the underlying memory should be the same, so Ada/GNAT could try a bit harder or just be honest and say that this is a mistake :slight_smile:

This is a valid point, but I’m really not interested in interoperating with Fortran, it was just the only thing I found that could affect the component layout. I would be perfectly happy with something like

type Matrix is array (1 .. 4, 1 .. 2) of Boolean with
   Storage_Layout => Column_Major;

I find interesting that you can be precise (and get guarantees) with record representation clauses, but there seems to be no similar capabilities for arrays.

The next step would be supporting packed symmetrical matrixes and three-diagonal matrices, video frame buffers etc… :rofl:

Note that even for something like

type Bit_Map is array (1 .. 8) of Boolean with Component_Size => 1, Size => 8;

the language doesn’t specify which index corresponds to which bit position. If you need to use indices to access specific bit positions, it’s probably best to use a private type with appropriate operations.