1
votes

My problem is that I created a protected Safe_Printer; but when I use it's procedure, it messes up the Put function. Every time it prints to a new line. How could I prevent this? Basically, I would like to print my matrix with it in a readable format.

Here is my code:

with Ada.Text_IO;
use Ada.Text_IO;

procedure main is

   type Matrix is array(integer range <>, integer range <>) of integer; 

    protected Safe_Printer is
        procedure Put(S: String);
        procedure Put(M: Matrix);
    end Safe_Printer;

    protected body Safe_Printer is

        procedure Put(S: String) is
        begin
            for I in S'range loop
                Put(S(I));
            end loop;
            New_Line;
        end Put;

        procedure Put(M:Matrix) is
        begin
            for I in m'range(1) loop
                for j in m'range(2) loop
                    put(integer'image(m(i,j)) & " ");
                end loop;
                new_line(1);
            end loop;
        end Put;
    end Safe_Printer;

m:Matrix := ((1,2,3),
            (4,5,6));
begin

    Safe_Printer.Put(m);


        for I in m'range(1) loop
            for j in m'range(2) loop
                put(integer'image(m(i,j)) & " ");
            end loop;
         new_line(1);
        end loop;

end main;

And my output is :

 1
 2
 3

 4
 5
 6

 1  2  3
 4  5  6
2

2 Answers

2
votes

Because the 'Image attribute returns a String, your implementation of Put(M : Matrix) invokes Safe_Printer.Put(S : String) in its inner loop; each matrix entry then gets a New_Line. One solution is to call the language defined Put explicitly in Safe_Printer.Put(M : Matrix):

procedure Put(M : Matrix) is
begin
    for I in M'range(1) loop
        for j in M'range(2) loop
            Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
        end loop;
        New_Line;
    end loop;
end Put;

As tested:

with Ada.Text_IO;
use Ada.Text_IO;

procedure main is

   type Matrix is array(integer range <>, integer range <>) of integer; 

    protected Safe_Printer is
        procedure Put(S : String);
        procedure Put(M: Matrix);
    end Safe_Printer;

    protected body Safe_Printer is

       procedure Put(S : String) is
       begin
           for I in S'range loop
               Put(S(I));
           end loop;
           New_Line;
       end Put;

        procedure Put(M : Matrix) is
        begin
            for I in M'range(1) loop
                for j in M'range(2) loop
                    Ada.Text_IO.Put(integer'image(M(i,j)) & " ");
                end loop;
                New_Line;
            end loop;
        end Put;
    end Safe_Printer;



M : Matrix := ((1,2,3),
               (4,5,6));
begin
    Safe_Printer.Put(M);
end main;

Console:

$ ./main
 1  2  3 
 4  5  6 

Alternatively, as @SimonWright comments, you could "remove the New_Line in Safe_Printer.Put(String)." The approach you choose will depend on the desired effect offered in the Safe_Printer specification. Also consider limiting the scope of the use clause.

0
votes

You should not call a 'potentially blocking operation' from within a protected object. The call to Text_IO.Put is a potentially blocking operation. See the Ada Reference Manual section 9.5.1. I am surprised your compiler didn't complain, actually.

One thing you can do in this situation is to use a task instead of a protected object.

For example:

with Ada.Text_IO;

procedure Main is

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

   task Safe_Printer is
      entry Put (S: in String);
      entry Put (M: in Matrix);
   end Safe_Printer;

   task body Safe_Printer is
      S: access String;
      M: access Matrix;
   begin
      loop
         select
            accept Put (S: in String) do
               Safe_Printer.S := new String (S'Range);
               Safe_Printer.S.all := S;
            end;
            Ada.Text_IO.Put_Line (S.all);
         or
            accept Put (M: in Matrix) do
               Safe_Printer.M := new Matrix (M'Range (1), M'Range (2));
               Safe_Printer.M.all := M;
            end;
            for I in M'Range (1) loop
               for J in M'Range (2) loop
                  Ada.Text_IO.Put (Integer'Image (M (I, J)) & " ");
               end loop;
               Ada.Text_IO.New_Line;
            end loop;
         or
            terminate;
         end select;
      end loop;
   end Safe_Printer;

   M: Matrix := ((1,2,3),
                 (4,5,6));
begin
   Safe_Printer.Put (M);
end Main;

An added advantage of this approach is that the calls to Text_IO.Put[_Line] are actually executed in parallel with the main task. A disadvantage is that (to be safe) the string or matrix to be printed is copied.

A better version of this code might use Unchecked_Deallocation or Ada.Containers.Indefinite_Holders to clean up after itself nicely.

Bonus question: What do you think the array bounds of the matrix M in the Main procedure are?