tags:

views:

213

answers:

3

Hello, I'm trying to follow a standard reference for opening files but running into a constraint_error at the line when I call Ada.Text_IO.Create(). It says "range check failed". Any help appreciated, here's the code:

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
USE Ada.Text_IO;
USE Ada.Integer_Text_IO;

PROCEDURE FileManip IS

   --Variables
   Start_Int : Integer;
   Stop_Int : Integer;
   Max_Length : Integer;

   --Output File
   MaxName : CONSTANT Positive := 80;
   SUBTYPE NameRange IS Positive RANGE 1..MaxName;

   OutFileName : String(NameRange) := (OTHERS => '#');
   OutNameLength : NameRange;
   OutData : File_Type;

   --Array
   TYPE Chain_Array IS ARRAY(1..500) OF Integer;
   Sum : Integer := 1;
BEGIN

--Inputs
   Ada.Text_IO.Put(Item => "Enter a starting Integer: ");
   Ada.Integer_Text_IO.Get(Item => Start_Int);
   Ada.Text_IO.New_Line;
   Ada.Text_IO.Put(Item => "Enter a stopping Integer: ");
   Ada.Integer_Text_IO.Get(Item => Stop_Int);
   Ada.Text_IO.New_Line;
   Ada.Text_IO.Put(Item => "Enter a Maximum Length to search: ");
   Ada.Integer_Text_IO.Get(Item => Max_Length);
   Ada.Text_IO.New_Line;
   Ada.Text_IO.Put(Item => "Enter a output file name > ");
   Ada.Text_IO.Get_Line(
      Item => OutFileName,
      Last => OutNameLength);
   Ada.Text_IO.Create(
      File => OutData,
      Mode => Ada.Text_IO.Out_File,
      Name => OutFileName(1..OutNameLength));
   Ada.Text_IO.New_Line;
+1  A: 

For each Integer you read, Ada.Integer_Text_IO.Get uses the default Width of zero, so it "skips any leading blanks, line terminators, or page terminators, then reads a plus sign if present or (for a signed type only) a minus sign if present, then reads the longest possible sequence of characters matching the syntax of a numeric literal without a point." A subsequent Ada.Text_IO.New_Line leaves the line terminator there for Ada.Text_IO.Get_Line to trip over, and zero is not within NameRange. Instead use Ada.Text_IO.Skip_Line. For example,

Ada.Text_IO.Put(Item => "Enter a starting Integer: ");
Ada.Integer_Text_IO.Get(Item => Start_Int);
Ada.Text_IO.Skip_Line;
Ada.Text_IO.Put(Item => "Enter a stopping Integer: ");
Ada.Integer_Text_IO.Get(Item => Stop_Int);
Ada.Text_IO.Skip_Line;
Ada.Text_IO.Put(Item => "Enter a Maximum Length to search: ");
Ada.Integer_Text_IO.Get(Item => Max_Length);
Ada.Text_IO.Skip_Line;
...
trashgod
Get reads "up to a line terminator" only if a non-zero value of Width is specified (refer to the linked reference). If Width is zero, the default case, then Integer_Text_IO.Get "reads the longest possible sequence of characters matching the syntax of a numeric literal without a point".
Marc C
Thanks for catching that; answer amended.
trashgod
+1  A: 

Tangentially related...

A common practice for acquiring interactive input is to avoid using the "Integer_Text_IO" and related packages for Gets, and instead use Get_Line and then do a conversion. E.g.:

   S : String(1..200);
   L : Natural;

   ...

   Ada.Text_IO.Put(Item => "Enter a starting Integer: ");
   Ada.Text_IO.Get_Line(Item => S, Last => L);
   Start_Int := Integer'Value(S(1..L));

This approach makes it easier to check for a user entering a C/R (L = 0), and while the numeric Get procedures only read characters so long as they conform to the syntax of a numeric literal, up to the end-of-line, by grabbing the whole line with Get_Line, you avoid having to deal with the end-of-line issue and can make sure that all of what was entered was a numeric literal (by catching the Constraint_Error exception if one is raised when evaluating the 'Value attribute).

And although it was more of a problem with earlier Ada compilers, the Get procedures didn't always work identically on different platforms due to different systems' conventions for designating end of line, end of file, etc. Get_Line, though, pretty much worked the same across all platforms, and so it and a subsequent string->numeric conversion was a widely recommended practice.

Marc C
+1 Although tangential to the problem, this is a more reliable idiom.
trashgod
+2  A: 

Likely it isn't the create that is giving you that problem, but an array range check on OutFileName(1..OutNameLength).

For that to happen, either 1 or OutNameLength would need to be out of the range specified for OutFileName. Since that was defined as RANGE 1..MaxName (via NameRange), we know it isn't the 1, so the culprit must be OutNameLength.

Looking around, it looks like you get that value from the Last parameter of a Get_Line. There is one situation where you can get a 0 out of that parameter: when you read a blank line. So my guess is that is what is happening.

T.E.D.