next up previous contents index
Next: Compatibility with C types Up: User Guide Previous: Data structures   Contents   Index

Input/Output

libaldor provides an I/O model inspired from the stream model of Java and C++. Data is written in text format on objects of type TextWriter, in binary format on objects of type BinaryWriter, read in text format from objects of type TextReader and read in binary format from objects of type BinaryReader. The standard streams stdin, stdout and stderr are constants exported by TextReader and TextWriter respectively, while bin, bout and berr are their binary counterparts. In addition, any File can be coerced into either text or binary readers or writers, any String can be coerced into text readers or writers, any PrimitiveMemoryBlock can be coerced into binary readers or writers, and you can create custom streams via the textReader, textWriter, binaryReader and binaryWriter functions.

The function $<<$ is used for both input and output: its binary version ``writer $<<$ data'' is for output, and its unary version ``$<<$ reader'' is for input, in which case the return type must be specified, either via an assigment to a variable, e.g. n:MachineInteger := $<<$ stdin, or via the @ construct, or via the context. Whether you are reading/writing text or serializing data depends on the reader/writer type (text or binary). For example, a libaldor version of the ``Hello world'' program is

#include "aldor"

import from TextWriter, String, Character;  -- Character needed for 'newline'
stdout << "Hello world!" << newline;
Text and binary writers can be flushed, either via the flush! function, or by sending the constant flush, exported by WriterManipulator. Thus, the two lines
flush!(stdout $<<$ "Hello world" $<<$ newline);
and
stdout $<<$ "Hello world" $<<$ newline $<<$ flush;
are equivalent. The manipulator endnl sends first a newline and then flushes the stream, so another alternative for the above is
stdout $<<$ "Hello world" $<<$ endnl;

When coercing strings and buffers to readers or writers, you should assign the resulting stream to a variable if you intend to read or write more than one value from the stream, since the coercion resets the stream to the beginning of the string. For example, if s is the String `` 12 56'', then

import from MachineInteger;
a:MachineInteger := << s::TextReader;             -- assigns 12 to a
b:MachineInteger := << s::TextReader;             -- assigns again 12 to b
while
import from MachineInteger;
p := s::TextReader;
a:MachineInteger := << p;                         -- assigns 12 to a
b:MachineInteger := << p;                         -- assigns 56 to b
A File is however not reset when coerced, so if the file ``mydata'' contains ``[1,2,3] [4,5,6]'', then both structures can be read by
import from File, String;
f := open("mydata", fileRead);
l:List MachineInteger := << f::TextReader;        -- assigns [1,2,3] to l
v:Array MachineInteger := << f::TextReader;       -- assigns [4,5,6] to v
close! f;
When coercing a String to a text writer or a PrimitiveMemoryBlock to a binary writer, you must ensure that the string or buffer is long enough to contain all the data that will be written to it, since libaldor does not protect you against writing past the end of the string or buffer, which is not extended by the write operation. You can either use new to create a large enough buffer, or write into an existing string or buffer. The type StringBuffer provides string that grow when you write into them, so use it when you do not know the amount of characters that will be written. For example:
import from Integer, String;
strw:StringBuffer := new();
strw::TextWriter << "20! = " << factorial 20 << null;
str := string strw;
produces the string ``20! = 2432902008176640000''. You can reuse the same StringBuffer in several calls in order to reuse the corresponding buffer rather than allocating new ones. When writing to a String or StringBuffer, you should in general write null (a constant exported by Character) after your data in order to terminate the string properly. Note that the debug version of libaldor performs bound checking on accesses into String and PrimitiveMemoryBlock, so it can verify whether you are writing past the end of a string or buffer. See the subsection on debugging for more information on using the debug library. Coercing a File to a reader or writer allocates memory, so it is advisable to assign the resulting stream to a variable. Unlike the ones for strings or buffers, those coercions do not reset the file to its beginning.

libaldor provides 2 categories for text input/output: InputType is for types whose elements can be read from a TextReader, and OutputType is for types whose elements can be written to a TextWriter. In addition, the single category SerializableType is for types whose elements can be serialized in binary format from a BinaryReader and to a BinaryWriter. All the arithmetic types provided by libaldor, as well as Byte, Character, String, MemoryBlock and CheckingMemoryBlock are InputType, OutputType and SerializableType, allowing you to read, write and serialize their elements. The data structures List T, Array T, PrimitiveArray T and HashTable(K,V) inherit whatever input and output capabilities that their parameters have.

Programs that perform input or output tend to repeatedly import the various stream types and accessories (manipulators, characters and strings). As an alternative to those imports, you can use #include "aldorio" in addition to #include "aldor", which does a global import of all the following: Character, String, File, TextReader, TextWriter, BinaryReader, BinaryWriter and WriterManipulator. So an alternative ``Hello world'' program would be:

#include "aldor"                              -- performs no import
#include "aldorio"                            -- imports all the I/O types

stdout << "Hello world!" << endnl;


next up previous contents index
Next: Compatibility with C types Up: User Guide Previous: Data structures   Contents   Index
Manuel Bronstein 2004-06-28