7.6 Input/Output
I/O facilities in Ada are not a part of the syntactic definition of the language. The constructs in the language have been used to define a set of packages for this purpose. These packages are not expected to meet all the I/O needs of all applications, in particular, embedded systems. They serve as a core subset that may be used on straightforward data and that can be used as examples of building I/O facilities upon the low-level constructs provided by the language. Providing an I/O definition that could meet the requirements of all applications and integrate with the many existing operating systems would result in unacceptable implementation dependencies. The types of portability problems encountered with I/O tend to be different for applications running with a host operating system versus embedded targets where the Ada run-time is self-sufficient. Interacting with a host operating system offers the added complexity of coexisting with the host file system structures (e.g., hierarchical directories), access methods (e.g., indexed sequential access method [ISAM]), and naming conventions (e.g., logical names and aliases based on the current directory). The section on Input/Output in ARTEWG (1986) provides some examples of this kind of dependency. Embedded applications have different dependencies that often tie them to the low-level details of their hardware devices.
The major defense against these inherent implementation dependencies in I/O is to try to isolate their functionality in any given application. The majority of the following guidelines are focused in this direction.
Name and Form Parameters
guideline
- Use constants and variables as symbolic actuals for the Name and Form parameters on the predefined I/O packages. Declare and initialize them in an implementation dependency package.
rationale
The format and allowable values of these parameters on the predefined I/O packages can vary greatly between implementations. Isolation of these values facilitates portability. Not specifying a Form string or using a null value does not guarantee portability because the implementation is free to specify defaults.
notes
It may be desirable to further abstract the I/O facilities by defining additional Create and Open procedures that hide the visibility of the Form parameter entirely (see Pappas 1985, 54-55).
File Closing
guideline
- Close all files explicitly.
rationale
The Ada Reference Manual (1995, §A.7) does not define what happens to external files after completion of the main subprogram (in particular, if corresponding files have not been closed).
The disposition of a closed temporary file may vary, perhaps affecting performance and space availability (ARTEWG 1986).
Input/Output on Access Types
guideline
- Avoid performing I/O on access types.
rationale
The Ada Reference Manual (1995, §A.7) does not specify the effects of I/O on access types. When such a value is written, it is placed out of reach of the implementation. Thus, it is out of reach of the reliability-enhancing controls of strong type checking.
Consider the meaning of this operation. One possible implementation of the values of access types is virtual addresses. If you write such a value, how can you expect another program to read that value and make any sensible use of it? The value cannot be construed to refer to any meaningful location within the reader's address space, nor can a reader infer any information about the writer's address space from the value read. The latter is the same problem that the writer would have trying to interpret or use the value if it is read back in. To wit, a garbage collection and/or heap compaction scheme may have moved the item formerly accessed by that value, leaving that value "pointing" at space that is now being put to indeterminable uses by the underlying implementation.
Package Ada.Streams.Stream_IO
guideline
- Consider using Sequential_IO or Direct_IO instead of Stream_IO unless you need the low-level, heterogeneous I/O features provided by Stream_IO.
rationale
Sequential_IO and Direct_IO are still well suited for processing homogeneous files. Additionally, in cases where the intent is to process homogeneous files, the use of Sequential_IO or Direct_IO has the advantage of enforcing this intent at compile time.
Stream_IO should be reserved for processing heterogeneous files. In this case, a file is not a sequence of objects of all the same type but rather a sequence of objects of varying types. To read a heterogeneous sequence of objects in the correct order requires some application-specific knowledge.
Current Error Files
guideline
- Consider using Current_Error and Set_Error for run-time error messages.
example
with Ada.Text_IO;
...
begin
Ada.Text_IO.Open (File => Configuration_File,
Mode => Ada.Text_IO.In_File,
Name => Configuration_File_Name);
exception
when Ada.Text_IO.Name_Error =>
Ada.Text_IO.Put_Line (File => Ada.Text_IO.Standard_Error,
Item => "Can't open configuration file.");
...
end;
rationale
The package Text_IO includes the concept of a current error file. You should report errors to the user through the associated subprograms Current_Error and Set_Error instead of the standard output facilities. In interactive applications, using the Text_IO error facilities increases the portability of your user interface.
notes
In a program with multiple tasks for I/O, you need to be careful of two or more tasks trying to set Current_Input, Current_Output, or Current_Error. The potential problem lies in unprotected updates to the "shared" state associated with a package, in this case, the package Text_IO. Guidelines 6.1.1 and 6.2.4 discuss the related issues of unprotected shared variables.