Non-visible declaration / bounded_strings and package

I am stumped. The stripped-down example contains a package with a function “get_strings” that reads the lines of “readme.txt” into an array of bounded strings. The “run_test” will compile and run without “Put_Line(To_String…)”. But with that line we get compile error shown in comments in run_test.adb below.

q_pkg.ads contents:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Bounded;

package Q_pkg is

  package B_str is new Ada.Strings.Bounded.Generic_Bounded_Length (Max => 100); use B_Str;
  type B_array is array (Natural range <>) of B_str.Bounded_String;
  function get_strings return B_array;

end Q_pkg;

q_pkg.adb contents:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Bounded;

package body Q_pkg is

  function get_strings return B_Array is

  Dummy : B_str.Bounded_String;

  N_lines : Integer := 0;
  F_In : File_Type;
  Input_File_Name : constant String := "readme.txt";

  begin

    Open (F_In, In_File, "readme.txt");
    while not End_Of_File (F_In) loop
      Dummy := To_Bounded_String(Get_Line(F_In));
      N_Lines := N_Lines+1;
    end loop;
    Reset (F_In);

    declare
      A1 : B_array (1 .. N_Lines);
    begin
      for I in A1'Range loop
        A1(I) := To_Bounded_String(Get_Line(F_In));
      end loop;
      return A1;
    end;
  end get_strings;
end Q_pkg;

and run_test.adb:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Strings; use Ada.Strings;
with Ada.Strings.Bounded;

with Q_pkg; use Q_pkg;

procedure run_test is

begin
  declare
    A : B_Array := get_strings;
  begin
    for I in A'Range loop
      Put_Line(Integer'Image(I));
     
      -- Put_Line(To_String(A(I)));
      -- uncommenting line above gives:
      -- run_test.adb:16:16: error: "To_String" is not visible
      -- run_test.adb:16:16: error: non-visible declaration from "b_str" at a-strbou.ads:130, instance at q_pkg.ads:7

    end loop;
  end;
end run_test;

readme.txt:

1
2
3

I must be missing something about bounded strings, etc. Can anybody see the issue?

To_String is declared in an instance of Ada.Strings.Bounded, which is Q_Pkg.B_Str. Thus its fully qualified name is Q_Pkg.B_Str.To_String. Add use B_Str to make it visible.

P.S. You can also rename it inside Q_Pkg.

P.P.S. Ada is not C. Never use End_Of_File.

Dmitry, Thank you! This makes perfect sense now that I see it.

Two follow-ups, if I may:

  1. How would I “rename it inside Q_Pkg”? I do not understand this.

  2. The “End_of_File” used above comes from:

What might be used instead of “End_of_File”.

Thanks in advance,
/James

function To_String (Source : B_Str.Bounded_String) return String
   renames B_Str.To_String;

You should read files once because there exist files that do not allow repositioning.

Instead of testing for file end simply read it in an infinite loop and catch Use End_Error exception. The patter is:

begin
   loop
      ... -- read file
   end loop;
exception
  when End_Error =>
      null;
end;

Note that you selected bounded string which means that it is likely be allocated out of the pool. Therefore you will get all on the stack and therefore you should expect stack overflow coming quite quickly. If not, you can write a function that utilize stack making recursive calls to get array in chunks. No extra pass of lines counting is needed.

The general rule is that bounded strings are basically useless. You either go full pool e.g. doing Unbounded_String + Vector. Or else you use an arena to allocate strings there if you want stay with stack keeping it as tight as possible. Arena can sit in a pool too, but allocated just once.

There is also middle way like having an array of access to string automatically freeing strings. It has the advantage that strings will be accessible directly like in case of arena. Unbounded_String is quite clumsy and inefficient to access.