Consequently, the conventions used to designate the target of an input-output operation should not necessarily distinguish between file names, device names, volume names, and so on. In addition, these conventions should not conflict with those of any operating system under which the language is implemented. Thus we need to reconcile the ability to communicate with files in arbitrary operating systems with the retention of as much portability as possible in the greater part of the program. This is done by distinguishing an external physical file, whose general properties - in particular the lifetime - are quite outside the realm of the Ada program, from an internal abstract Ada object, upon which the program can operate with a well-defined set of abstract operations.
The external file is conveniently designated by a string, whose interpretation is completely implementation-dependent. A string is used for this purpose because of its generality and accommodating nature. This string is thus a parameter (called NAME) of those procedures that need to identify an external file. There are in fact two of these: CREATE which establishes a new external file, and OPEN which refers to an existing external file. In addition, it has proved convenient to provide a second string parameter (called FORM), through which arbitrary auxiliary information can be supplied. The default value for each of these parameters is the null string; in the case of the name, this is conveniently taken to indicate a temporary and anonymous file, whereas in the case of the form it merely indicates the absence of explicit auxiliary information.
Within the Ada program, the file is referred to via an Ada object of a limited private type. The type is limited (rather than just private) so that the user cannot make arbitrary copies of file objects, and thereby prevent the file package from having complete control over file access and over the deallocation of internal buffer storage; for a more extensive discussion of this technique see section 9.2.3.
The connection between the internal file object and the external file is made by passing the object as a further parameter to CREATE or OPEN. All access to the external file is then made by referring to the internal object. This includes calls of CLOSE and DELETE, which sever the connection between the external file and the internal file object and, in the case of DELETE, actually delete the external file itself.
In this section... |
type FILE_TYPE(MODE : FILE_MODE) is limited private;
We therefore reject the first approach, and consider approaches using a single type. The use of a discriminant to govern the access mode appears neat; it solves the problems of multiple types, and keeps the access information within the type concept but yet visible to the user. However, since the type is limited, the user is unable to change the discriminant anyway, and so this approach is unfruitful.
We thus come to the third of the possibilities outlined at the beginning of this section. This offers a practical solution, which has been adopted.
There is a single type, FILE_TYPE, which applies to all files and, in addition, a quite separate property of each file, which governs its access mode. This property is set when a file is opened or created, through a parameter to OPEN or CREATE, and can also be changed by calling the procedure RESET. The specifications of these procedures are thus
procedure CREATE (FILE : in out FILE_TYPE; MODE : in FILE_MODE := OUT_FILE; NAME : in STRING := ""; FORM : in STRING := ""); procedure OPEN (FILE : in out FILE_TYPE; MODE : in FILE_MODE; NAME : in STRING; FORM : in STRING := ""); procedure RESET (FILE : in out FILE_TYPE; MODE : in FILE_MODE); |
Note that the MODE has a default value of OUT_FILE in the case of CREATE. This is sensible, since it is appropriate that the first action on a newly created file is to write to it. (In the case of direct files, the default is INOUT_FILE; this mode does not apply to sequential and text files.)
The procedure RESET repositions a file at the beginning, and can also be used to change the mode so that, having written a file, we can now read it. There is also a further overloading of RESET which omits the second parameter and just repositions the file without changing its mode.
We conclude this section by observing that, although the system does not give the compilation time security that one would have liked, nevertheless a special secure system of the first category could be built on top of the more flexible current one using derived types. However, the reverse is not possible, and so the present solution has the additional merit of leaving more options open for special circumstances.
PUT(RESULTS, "value is "); PUT(RESULTS, VALUE); NEW_LINE(RESULTS); |
in which the repetition of the file name RESULTS is rather verbose.
In order to overcome this, the various procedures have two overloaded forms, one with the file parameter and one without - for example, in the case of the type character:
procedure PUT(FILE : in FILE_TYPE; ITEM : in CHARACTER); procedure PUT(ITEM : in CHARACTER); |
If the file parameter is omitted, an appropriate default file is used - there is one for input and one for output. These default files are initially set to two standard files, but can be changed by the user. Thus we can more conveniently write
SET_OUTPUT(RESULTS);
followed by
PUT("value is "); PUT(VALUE); NEW_LINE;
The subprograms provided for the manipulation of the default files are
procedure SET_OUTPUT(FILE : in FILE_TYPE); function STANDARD_OUTPUT return FILE_TYPE; function CURRENT_OUTPUT return FILE_TYPE; |
with corresponding subprograms for input. As the names suggest, SET_OUTPUT sets the default to the specified file, STANDARD_OUTPUT returns the initial standard file, and CURRENT_OUTPUT returns the file that is the present default file.
It is thus possible to set the default file to that required locally, and then to reset it to the standard, by:
SET_OUTPUT(local_file); -- do the I/O SET_OUTPUT(STANDARD_OUTPUT); |
A more general requirement (in a widely used standard subprogram, perhaps) might be to surround the local use of a default file by statements which preserve and then restore the existing default file. The function CURRENT_OUTPUT is provided so that this can be done.