Ada Programming/Libraries/Ada.Streams.Stream IO
“The packages Sequential_IO and Direct_IO have not proved to be sufficiently flexible for some applications because they process only homogenous files.” Ada 95 Rationale: Annex A.4.1 Stream Input and Output
“[Serialization is] the ability to write the complete state of an object (including any objects it refers to) to an output stream, and then recreate that object at some later time by reading its serialized state from an input stream.” Java in a Nutshell, David Flanagan, ,2nd ed, O’Reilly & Associates, Inc., 1997, p. 97.
A stream is an entity that can consume or produce elements of raw data, typically bytes.
- procedure Write
- procedure Read
Every type has a default encode–and–write procedure named by the attribute T'Write and a default read–and–decode procedure named by the attribute T'Read.
For example, suppose an input file mixed.data contains several groups of Float values in internal form, with each group preceded by an Integer value giving the number of values in the group. The following program writes the sum of each group in textual form to the standard output file (Ada as a Second Language, 2nd ed., Cohen, p 794 – corrected from Errata):
with Ada.Float_Text_IO; with Ada.Streams.Stream_IO; procedure Sum_Groups is Input_File : Ada.Streams.Stream_IO.File_Type; Input_Stream : Ada.Streams.Stream_IO.Stream_Access; Group_Size : Integer; -- Number of data items Sum : Float; -- Sum of data items Next : Float; -- Input data item begin Ada.Streams.Stream_IO.Open(Input_File, Ada.Streams.Stream_IO.In_File, "mixed.data"); Input_Stream := Ada.Streams.Stream_IO.Stream(Input_File); Process_Group: while not Ada.Streams.Stream_IO.End_Of_File(Input_File) loop Integer'Read(Input_Stream, Group_Size); Sum := 0.0; -- Initialize sum of group Get_Group_Data: for I in 1..Group_Size loop Float'Read(Input_Stream, Next); Sum := Sum + Next; end loop Get_Group_Data; Ada.Float_Text_IO.Put(Sum); end loop Process_Group; Ada.Streams.Stream_IO.Close(Input_File); end Sum_Groups;
Streams for Limited or User-defined Types
The programmer can override the
'Read attributes for types whose default procedures do not provide the desired behavior. These types are typically access types or composite types with access–type subcomponents.
Typically, the programmer does not call the Read and Write procedures manipulating sequences of raw data directly. Instead, the programmer:
- Creates read and write procedures for types
- Associates procedures with corresponding attributes
Encoding a date record (from Ada 95 Rationale, Annex A.4.1, p A–27)
type Day_Number is range 1..31; type Month_Name is (January, February, March, April, May, June, July, August, September, October, November, December); type Date_Record is record Day : Day_Number; Month : Month_Name; Year : Integer; end record;
Then, the programmer creates an output file normally, and obtains access to associated date stream.
Some_Date : Date_Record; Date_File : Ada.Streams.Stream_IO.File_Type; Date_Stream : Ada.Streams.Stream_IO.Stream_Access; begin Ada.Streams.Stream_IO.Create(Date_File); Date_Stream := Ada.Streams.Stream_IO.Stream(Date_File);
To output the data, the programmer invokes the
Write attribute for the
Date_Record on the value of the date to be written:
For a simple record such as
Date_Record the predefined
Write attribute simply calls the write attributes for each component in order.
The programmer can provide a version of
Write to output the month name as, say, an integer:
First, define the date write procedure and associate
procedure Date_Write(Stream : access Root_Stream_Type'Class; Item : in Date_Record) is begin Integer'Write(Stream, Item.Day); Integer'Write(Stream, Month_Name'Pos(Item.Month) + 1); Integer'Write(Stream, Item.Year); end Date_Write; for Date_Record'Write use Date_Write;
Note that, instead of redefining
Date_Record'Write, we could have simply redefined
Month_Name'Write, and this would be called indirectly.