Question

This question is a follow-up of the post at

Ada file operation: instantiation and exception

about writing to files in Ada.

I chose to place this question in a separate post so that it'll become visible to more people as I already accepted an answer on a slightly different issue (which was on exceptions in file handling) in that aforementioned post.

WITH Ada.Sequential_IO;
WITH Ada.Float_Text_IO;

PROCEDURE TEST is

package Seq_Float_IO is new Ada.Sequential_IO (Element_Type => Float);
X_File : Seq_Float_IO.File_Type;
File_Name : String;


procedure Open_Data(File : in out Seq_Float_IO.File_Type; 
Name : in String) is

BEGIN

   begin
      Seq_Float_IO.Open (
         File => File,
         Mode => Seq_Float_IO.Append_File,
         Name => File_Name );
   exception
      when Seq_Float_IO.Name_Error =>
      Seq_Float_IO.Create (
         File => File,
         Mode => Seq_Float_IO.Out_File,
         Name => File_Name);
   end;

END Open_Data;


x        : CONSTANT Float := 2.0;


BEGIN --main program
   Open_Data(X_File, "xvalues.dat");
   Seq_Float_IO.Write(File => X_File,Item => x);

   Seq_Float_IO.Close(File => X_File);
END TEST;

On compiling the above I get an error as follows:

  1. X_File : Seq_Float_IO.File_Type;
    1. File_Name : String; |

      unconstrained subtype not allowed (need initialization) provide initial value or explicit array bounds

I don't know 2 things:

  1. I have File_Name : String; as I want to be able to write to different files. So I want a general string and not something like:

File_Name : CONSTANT String := "one_File_Only.dat"

  1. Would it be better to save the procedure Open_Data in separate ads and adb (for the body) files?

Thanks a lot...


NEW...

I've modified the code as follows:

WITH Ada.Sequential_IO;

PROCEDURE TEST1 is

package Seq_Float_IO is new Ada.Sequential_IO (Element_Type => Float);
X_File, Y_File : Seq_Float_IO.File_Type;
Name_X : CONSTANT String := "domainvalues.dat";
Name_Y : CONSTANT String := "ordinatevalues.dat";


procedure Open_Data(File : in out Seq_Float_IO.File_Type; Name : in String) is

BEGIN

   begin
    Seq_Float_IO.Open (
         File => File,
         Mode => Seq_Float_IO.Append_File,
         Name => Name_X );
    exception
      when Seq_Float_IO.Name_Error =>
         Seq_Float_IO.Create (
            File => File,
            Mode => Seq_Float_IO.Out_File,
            Name => Name_X);
   end;

END Open_Data;


x        : CONSTANT Float := 2.0;


BEGIN --main program
   Open_Data(File => X_File, Name => Name_X);
   Seq_Float_IO.Write(File => X_File, Item => x);
   Seq_Float_IO.Close(File => X_File);

   Open_Data(File => Y_File, Name => Name_Y);
  Seq_Float_IO.Write(File => Y_File, Item => x);
  Seq_Float_IO.Close(File => Y_File);

END TEST1;

As you see I have

Seq_Float_IO.Open (
             File => File,
             Mode => Seq_Float_IO.Append_File,
             Name => Name_X );

I have put Name_X as the parameter that Name is taking but this is not right as I should be able to pass in a general name which can be either Name_X or Name_Y. Sorry guys, I can't figure out what to put here.

I would much appreciate your help. Thanks

Was it helpful?

Solution

The thing about plain String in Ada is that a particular string, like your File_Name, has to be fixed-length; but different Strings can be of different length.

You can write

S1 : String := "1234";
S2 : String := "12345";

in which case S1 is of length 4, and assignments to it have to be of length 4. You can write

S1 := "abcd";

but if you try to write

S1 := "pqrst";

or

S1 := S2;

you will get a Constraint_Error.

In the case of String parameters to subprograms, like your Open_Data, the String parameter Name takes on the length -- and of course the value! of the actual parameter in the call. So you can say

Open_Data (X_File, "x.dat");
Open_Data (Y_File, "a_very_long_name.dat");

You were having problems earlier with

procedure Open_Data(File : in out Seq_Float_IO.File_Type; 
                    Name : in String) is
begin
   Seq_Float_IO.Open (File => File,
                      Mode => Seq_Float_IO.Append_File,
                      Name => ????);

I'm reluctant to just tell you the answer, so -- consider the File => File part. The first File is the name of the formal parameter of Seq_Float_IO.Open and the second File is what is to be passed, in this case Open_Data's File parameter.

It might help if I point out that I could have written the calls above as

Open_Data (File => X_File, Name => "x.dat");
Open_Data (File => Y_File, Name => "a_very_long_name.dat");

OTHER TIPS

@Simon Wright's answer is correct, and you may find it helpful to compare his answer to the second one I wrote earlier. Note that if you had

Name_X : constant String := "domainvalues.dat";
Name_Y : constant String := "ordinatevalues.dat";

Either string, Name_X or Name_Y, could be used as the actual Name parameter to Open_Data. The formal parameter, Name, is of type String. String is unconstrained, and it may be any (implementation-defined) maximum length. In contrast, Name_X and Name_Y each have a fixed length determined by their initial assignment.

Addendum: You wrote a subprogram with a formal parameter (Name) of type String, having this signature

procedure Open_Data(
    File : in out Seq_Float_IO.File_Type;
    Name : in String) is ...

In the implementation, you want to forward to Open the String you received as the actual parameter (Name), not the name of a global constant (Name_X).

Seq_Float_IO.Open (
    File => File,
    Mode => Seq_Float_IO.Append_File,
    Name => Name );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top