14.3 Stream IO of LINT Objects

Team-Fly

14.3 Stream I/O of LINT Objects

The classes contained in the C++ standard library such as istream and ostream are abstractions of input and output devices derived from the base class ios. The class iostream is in turn derived from istream and ostream, and it enables both writing and reading of its objects.[1] Input and output take place with the help of the insert and extract operators "<<" and ">>" (cf. [Teal], Chapter 8). These arise through overloading the shift operators, for example in the form

   ostream& ostream::operator<< (int i);   istream& istream::operator>> (int& i); 

in which they enable output, respectively input, of integer values through expressions of the form

   cout << i;   cin >> i; 

As special objects of the classes ostream and istream, cout and cin represent the same abstract files as the objects stdout and stdin of the standard C library.

The use of the stream operators "<<" and ">>" for input and output makes it unnecessary to consider particular properties of the hardware in use. In and of itself this is nothing new, for the C function printf() behaves the same way: A printf() instruction should always, regardless of platform, lead to the same result. However, above and beyond the altered syntax, which is oriented to the metaphorical image of the insertion of objects into a stream, the advantages of the C++ implementation of streams lie in the strict type checking, which in the case of printf() is possible only to a limited degree, and in its extensibility. In particular, we make use of the latter property by overloading the insert and extract operators so that they support input and output of LINT objects. To this end the class LINT defines the following stream operators:

   friend ostream& operator<< (ostream& s, const LINT& ln);   friend fstream& operator<< (fstream& s, const LINT& ln);   friend ofstream& operator<< (ofstream& s, const LINT& ln);   friend fstream& operator>> (fstream& s, LINT& ln);   friend ifstream& operator>> (ifstream& s, LINT& ln); 

A simple formulation of the overloaded insert operators for the output of LINT objects might look something like the following:

   #include <iostream.h>   ostream& operator<< (ostream& s, const LINT& ln)   {     if (!ln.init)       LINT::panic (E_LINT_VAL, "ostream operator <<", 0, __LINE__);     s << xclint2str (ln.n_l, 16, 0) << endl;     s << ld (ln) << " bit" << endl;     return s;   } 

The operator << thus defined outputs the digits of a LINT object as hexadecimal values and adds the binary length of the number in a separate line. In the next section we shall consider the possibilities of improving the appearance of the output of LINT objects with the aid of formatting functions, and we shall also use manipulators to make the output customizable.

14.3.1 Formatted Output of LINT Objects

In this section we shall make use of the base class ios of the C++ standard library and of its member functions to define our own LINT-specific formatting functions for the purpose of controlling the output format of LINT objects. Furthermore, we shall create manipulators that will make the customization of the output format for LINT objects as simple as it is for the standard types defined in C++.

The crucial point in the creation of formatted output of LINT objects is the possibility of setting formatting specifications that will be handled by the insert operator. To this end we shall consider the mechanism provided for the class ios (for details see [Teal], Chapter 6, and [Pla2], Chapter 6), whose member function xalloc() in the objects of the classes derived from ios allocates a status variable of type long and returns an index to this status variable also of type long. We store this index in the long variable flagsindex. By means of this index the member function ios::iword() can be used to access reading and writing to the allocated status variable (cf. [Pla2], page 125).

To ensure that this takes place before a LINT object is output, we define, in the file flintpp.h, the class LintInit as follows:

 class LintInit   {     public:       LintInit (void);   };   LintInit::LintInit (void)   {     // get index to long status variable in class ios     LINT::flagsindex = ios::xalloc();     // set the default status in cout and in cerr     cout.iword (LINT::flagsindex) =     cerr.iword (LINT::flagsindex) =       LINT::lintshowlength|LINT::linthex|LINT::lintshowbase;   } 

The class LintInit has as its only element the constructor LintInit::LintInit(). Furthermore, in the class LINT we define a member datum setup of type LintInit, which is initialized via the constructor LintInit::LintInit(). The call to xalloc() takes place within this initialization, and the status variable thereby allocated is given the established standard output format for LINT objects. In the following we shall show a section of the LINT class declaration, which contains the declaration of LintInit() as a friend of LINT, the declaration of the variables flagsindex and setup, and various status values as enum types:

 class LINT   {     public:       // ...       enum {          lintdec = 0x10,          lintoct = 0x20,          linthex = 0x40,          lintshowbase = 0x80,          lintuppercase = 0x100,          lintbin = 0x200,          lintshowlength = 0x400       };       // ...       friend LintInit::LintInit (void);       // ...     private:       // ...       static long flagsindex;       static LintInit setup;       // ...   }; 

Setting the variable setup as static has the effect that this variable exists only once for all LINT objects and thus the associated constructor LintInit() is called only once.

We would like now to pause for a moment and consider what all this effort nets us. Setting the output format could just as well be managed via a status variable, which as a member of LINT would be much simpler to deal with. The decisive advantage of the method that we have chosen is that the output format can be set for each output stream separately and independently of the others (cf. [Pla2], page 125), which could not be accomplished with an internal LINT status variable. This is done through the power of the class ios, whose mechanisms we employ for such purposes.

Now that the necessary preliminaries have been taken care of, we can define the status functions as member functions of LINT. These are displayed in Table 14.1.

We shall consider as an example of the implementation of the status functions the function LINT::setf(), which returns the current value of the status variable as a long with reference to an output stream:

Table 14.1: LINT status functions and their effects

Status function

Explanation

 static long LINT::flags (void); 

read the status variable with reference to cout

 static long LINT::flags (ostream&); 

read the status variable with reference to an arbitrary output stream

 static long LINT::setf (long); 

set individual bits of the status variable with reference to cout and return the previous value

 static long LINT::setf (ostream&, long); 

set individual bits of the status variable with reference to an arbitrary output stream and return the previous value

 static long LINT::unsetf (long); 

restore individual bits of the status variable with reference with reference to cout and return the previous value

 static long LINT::unsetf (ostream&, long); 

restore individual bits of the status variable with reference to an arbitrary output stream and return the previous value

 static long LINT::restoref (long); 

set the status variable with reference to cout with a value and return the previous value

 static long LINT::restoref (ostream&, long); 

set the status variable with reference to an arbitrary output stream with a value and return the previous value

 long LINT::setf (ostream& s, long flag)   {   long t = s.iword (flagsindex);   // the flags for the basis of the numerical representation   //are mutually exclusive   if (flag & LINT::lintdec)     {     s.iword (flagsindex) = (t & ~LINT::linthex & ~LINT::lintoct                   & ~LINT::lintbin) | LINT::lintdec;     flag ^= LINT::lintdec;     }   if (flag & LINT::linthex)     {     s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::lintoct                   & ~LINT::lintbin) | LINT::linthex;     flag ^= LINT::linthex;     }   if (flag & LINT::lintoct)     {     s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::linthex                   & ~LINT::lintbin) | LINT::lintoct;     flag ^= LINT::lintoct;     }   if (flag & LINT::lintbin)     {     s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::lintoct                   & ~LINT::linthex) | LINT::lintbin;     flag ^= LINT::lintbin;     }   // all remaining flags are mutually compatible   s.iword (flagsindex) |= flag;   return t;   } 

With the help of these and the remaining functions of Table 14.1 we can determine the output formats in the following. First, the standard output format represents the value of a LINT object as a hexadecimal number in a character string, where the output fills as many lines on the screen as required by the number of digits of the LINT object. In an additional line the number of digits of the LINT object is displayed flush left. The following additional modes for output of a LINT object have been implemented:

  1. Base for the representation of digits

    The standard base for the representation of digits of LINT objects is 16, and for the representation of the length it is 10. This default for LINT objects can be set for the standard output stream cout to a specified base by a call to

       LINT::setf (LINT::base); 

    and to

       LINT::setf (ostream, LINT::base); 

    for an arbitrary output stream. Here base can assume any one of the values

       linthex, lintdec, lintoct, lintbin, 

    which denote the corresponding output format. A call to LINT::setf(lintdec), for example sets the output format to decimal digits. The base for the representation of the length can be set with the function

       ios::setf (ios::iosbase); 

    with iosbase = hex, dec, oct.

  2. Display of the prefix for the numerical representation

    The default is for a LINT object to be displayed with a prefix indicating how it is represented. A call to

       LINT::unsetf (LINT::lintshowbase);   LINT::unsetf (ostream, LINT::lintshowbase); 

    changes this setting.

  3. Display of hexadecimal digits in uppercase letters

    The default is the display of hexadecimal digits and the display of the prefix 0x for a hexadecimal representation in lowercase letters abcdef. However, a call to

       LINT::setf (LINT::lintuppercase);   LINT::setf (ostream, LINT::lintuppercase); 

    changes this, so that the prefix 0X and uppercase letters ABCDEF are displayed.

  4. Display of the length of a LINT object

    The default is the display of the binary length of a LINT objects. This can be changed by a call to

       LINT::unsetf (LINT::lintshowlength);   LINT::unsetf (ostream, LINT::lintshowlength); 

    so that the length is not displayed.

  5. Restoring the status variable for the numerical representation

    The status variable for the formatting of a LINT object can be restored to a previous value oldflags by a call to the two functions

       LINT::unsetf (ostream, LINT::flags(ostream));   LINT::setf (ostream, oldflags); 

    Calls to these two functions are collected in the overloaded function restoref():

       LINT::restoref (flag);   LINT::restoref (ostream, flag); 

Flags can be combined, as in the call

  LINT::setf (LINT::bin | LINT::showbase); 

This, however, is permitted only for flags that are not mutually exclusive.

The output function that finally generates the requested representational format for LINT objects is an extension of the operator ostream& operator <<(ostream& s, LINT ln) already sketched above, which evaluates the status variables of the output stream and generates the appropriate output. For this the operator uses the auxiliary function lint2str() contained in flintpp.cpp, which in turn calls the function xclint2str_l() to represent the numerical value of a LINT object in a character string:

 ostream& operator << (ostream& s, const LINT& ln)   {     USHORT base = 16;     long flags = LINT::flags (s);     char* formatted_lint;     if (!ln.init) LINT::panic (E_LINT_VAL, "ostream operator<<", 0, __LINE__);     if (flags & LINT::linthex)       {          base = 16;       }     else       {          if (flags & LINT::lintdec)            {               base = 10;            }          else            {               if (flags & LINT::lintoct)                 {                   base = 8;                 }               else                 {                   if (flags & LINT::lintbin)                     {                        base = 2;                     }                 }            }       }     if (flags & LINT::lintshowbase)       {          formatted_lint = lint2str (ln, base, 1);       }     else       {          formatted_lint = lint2str (ln, base, 0);       }     if (flags & LINT::lintuppercase)       {          strupr_l (formatted_lint);       }     s << formatted_lint << flush;     if (flags & LINT::lintshowlength)       {          long _flags = s.flags (); // get current status          s.setf (ios::dec);// set flag for decimal display          s << endl << ld (ln) << " bit" << endl;          s.setf (_flags); // restore previous status       }       return s;   } 

14.3.2 Manipulators

Building on the previous mechanisms, we would like in this section to obtain more convenient possibilities for controlling the output format for LINT objects. To this end we use manipulators, which are placed directly into the output stream and thus display the same effects as occur in calling the above status functions. Manipulators are addresses of functions for which there exist special insert operators that on their part accept a pointer to a function as argument. As an example we consider the following function:

   ostream& LintHex (ostream& s)   {     LINT::setf (s, LINT::linthex);     return s;   } 

This function calls the status function setf(s, LINT::linthex) in the context of the specified output stream ostream& s and thereby effects the output of LINT objects as hexadecimal numbers. The name LintHex of the function without parentheses is viewed as a pointer to the function (cf. [Lipp], page 202) and can be set in an output stream as a manipulator with the help of the insert operator

   ostream& ostream::operator<< (ostream& (*pf)(ostream&))   {     return (*pf)(*this);   } 

defined in the class ostream:

   LINT a ("0x123456789abcdef0");   cout << LintHex << a;   ostream s;   s << LintDec << a; 

The LINT manipulator functions operate according to the same pattern as the standard manipulators in the C++ library, for example dec, hex, oct, flush, and endl: The insert operator << simply calls the manipulator function LintHex() or LintDec() at the appropriate place. The manipulators ensure that the status flags belonging to the output streams cout, respectively s, are set. The overloaded operator<< for the output of LINT objects takes over the representation of the LINT object a in the requested form.

The format settings for the output of LINT objects can all be carried out with the help of the manipulators presented in Table 14.2.

Table 14.2: LINT manipulators and their effects

Manipulator

Effect: form of the output of LINT values

LintBin

as binary numbers

LintDec

as decimal numbers

LintHex

as hexadecimal numbers

LintOct

as octal numbers

LintLwr

with lowercase letters a,b,c,d,e,f for hexadecimal representation

LintUpr

with uppercase letters A,B,C,D,E,F for hexadecimal representation

LintShowbase

with prefix for the numerical representation (0x or 0X for hexadecimal, 0b for binary)

LintNobase

without prefix for numerical representation

LintShowlength

indicating the number of digits

LintNolength

without indicating the number of digits

In addition to the manipulators of Table 14.2, which require no argument, the manipulators

   LINT_omanip<int> SetLintFlags (int flags) 

and

   LINT_omanip<int> ResetLintFlags (int flags) 

are available, which can be used as alternatives to the status functions LINT::setf() and LINT::unsetf():

   cout << SetLintFlags (LINT::flag) << ...; // turn on   cout << ResetLintFlags (LINT::flag) << ...; // turn off 

For the implementation of these manipulators the reader is referred to the sources (flintpp.h and flintpp.cpp) in connection with the explanation of the template class omanip<T> in [Pla2], Chapter 10. The LINT flags are shown once again in Table 14.3.

Table 14.3: LINT flags for output formatting and their effects

Flag

Value

lintdec

0x010

lintoct

0x020

linthex

0x040

lintshowbase

0x080

lintuppercase

0x100

lintbin

0x200

lintshowlength

0x400

We shall now clarify the use of the format functions and manipulators by means of the following example:

  #include "flintpp.h"  #include <iostream.h>  #include <iomanip.h>  main()  {    LINT n ("0x0123456789abcdef"); // LINT number with base 16    long deflags = LINT::flags(); // store flags    cout << "Default representation: " << n << endl;    LINT::setf (LINT::linthex | LINT::lintuppercase);    cout << "hex representation with uppercase letters: " << n << endl;    cout << LintLwr << "hex representation with lowercase letters: " << n << endl;    cout << LintDec << "decimal representation: " << n << endl;    cout << LintBin << "binary representation: " << n << endl;    cout << LintNobase << LintHex;    cout << "representation without prefix: " << n << endl;    cerr << "Default representation Stream cerr: " << n << endl;    LINT::restoref (deflags);    cout << "default representation: " << n << endl;    return;  } 

14.3.3 File I/O for LINT Objects

Functions for the output of LINT objects to files and functions for reading them are unavoidable for practical applications. The input and output classes of the C++ standard library contain member functions that permit the setting of objects into an input or output stream for file operations, so we are fortunate in that we can use the same syntax as we used above. The operators needed for output to files are similar to those of the last section, where, however, we can do without the formatting.

We define the two operators

   friend ofstream& operator<< (ofstream& s, const LINT& ln);   friend fstream& operator<< (fstream& s, const LINT& ln); 

for output streams of the class ofstream and for streams of the class fstream, which supports both directions, that is, both input and output. Since the class ofstream is derived from the class ostream, we can use its member function ostream::write() to write unformatted data to a file. Since only the digits of a LINT object that are actually used are stored, we can deal sparingly with the storage space of the data medium. Here the USHORT digits of the LINT object are actually written as a sequence of UCHAR values. To ensure that this always occurs in the correct order, independent of the numerical representation scheme of a particular platform, an auxiliary function is defined that writes a USHORT value as a sequence of two UCHAR types. This function neutralizes the platform-specific ordering of the digits to base 256 in memory and thereby allows data that were written on one computer type to be read on another that possibly orders the digits of a number differently or perhaps interprets them differently when they are read from mass storage. Relevant examples in this connection are the little-endian and big-endian architectures of various processors, which in the former case order consecutive increasing memory addresses in increasing order, and in the latter case do so in decreasing order.[2]

 template <class T>   int write_ind_ushort (T& s, clint src)   {     UCHAR buff[sizeof(clint)];     unsigned i, j;     for (i = 0, j = 0; i < sizeof(clint); i++, j = i << 3)       {          buff[i] = (UCHAR)((src & (0xff << j)) >> j);       }     s.write (buff, sizeof(clint));     if (!s)       {          return -1;       }      else       {          return 0;       }   } 

The function write_ind_ushort() returns in the case of error the value 1, while it returns 0 if the operation is successful. It is implemented as a template, so that it can be used with both ofstream objects and fstream objects. The function read_ind_ushort() is created as its counterpart:

   template <class T>   int read_ind_ushort (T& s, clint *dest)   {     UCHAR buff[sizeof(clint)];     unsigned i;     s.read (buff, sizeof(clint));     if (!s)       {          return -1;       }     else       {         *dest = 0;         for (i = 0; i < sizeof(clint); i++)           {              *dest |= ((clint)buff[i]) << (i << 3);           }         return 0;      }   } 

The output operators now use this neutral format to write from a LINT object to a file. To elucidate the situation we shall present the implementation of the operator for the class ofstream.

 ofstream& operator<< (ofstream& s, const LINT& ln)   {     if (!ln.init)       {          LINT::panic (E_LINT_VAL, "ofstream operator<<", 0, __LINE__);       }     for (int i = 0; i <= DIGITS_L (ln.n_l); i++)       {          if (write_ind_ushort (s, ln.n_l[i]))            {               LINT::panic (E_LINT_EOF, "ofstream operator<<", 0, __LINE__);            }       }     return s;   } 

Before a LINT object is written to a file, the file must be opened for writing, for which one could use the constructor

   ofstream::ofstream (const char *, openmode) 

or the member function

   ofstream::open (const char *, openmode) 

In each case the ios flag ios::binary must be set, as in the following example:

   LINT r ("0x0123456789abcdef");   // ...   ofstream fout ("test.io", ios::out | ios::binary);   fout << r << r*r;   // ...   fout.close(); 

The importation of a LINT object from a file is effected in a complementary way, with analogous operators, to that of output of a LINT object to a file.

   friend ifstream& operator >> (ifstream& s, LINT& ln);   friend fstream& operator >> (fstream& s, LINT& ln); 

Both operators first read a single value, which specifies the number of digits of the stored LINT object. Then the corresponding number of digits are read in. The USHORT values are read according to the above description under the action of the function read_ind_ushort():

   ifstream& operator>> (ifstream& s, LINT& ln)   {     if (read_ind_ushort (s, ln.n_l))       {          LINT::panic (E_LINT_EOF, "ifstream operator>>", 0, __LINE__);       }     if (DIGITS_L (ln.n_l) < CLINTMAXSHORT)       {          for (int i = 1; i <= DIGITS_L (ln.n_l); i++)            {               if (read_ind_ushort (s, &ln.n_l[i]))                 {                   LINT::panic (E_LINT_EOF, "ifstream operator>>", 0, __LINE__);                 }            }       }     // No paranoia! Check the imported value!     if (vcheck_l (ln.n_l) == 0)       {          ln.init = 1;       }     else       {          ln.init = 0;       }   return s;   } 

To open a file from which the LINT object is to be read it is again necessary to set the ios flag ios::binary:

   LINT r, s;   // ...   ifstream fin;   fin.open ("test.io", ios::in | ios::binary);   fin >> r >> s;   // ...   fin.close(); 

In the importation of LINT objects the insert operator >> checks whether the values read represent the numerical representation of a valid LINT object. If this is not the case, the member datum init is set to 0, and the specified target object is thereby marked as "uninitialized." On the next operation on this object the LINT error handler is invoked, which is what we shall study in more detail in the next chapter.

[1]We use this name of the stream classes as a synonym for the terms now used in the C++ standard library, with which the class names known up to now are prefixed with basic_. The justification for this comes from the standard library itself, where the class names known hitherto are provided with corresponding typedefs (cf. [KSch], Chapter 12).

[2]Two bytes Bi and Bi+1 with addresses i and i + 1 are interpreted in the little-endian representation as USHORT value w = 28Bi+1 + Bi and in the big-endian representation as w = 28Bi + Bi+1. The analogous situation holds for the interpretation of ULONG values.


Team-Fly


Cryptography in C and C++
Cryptography in C and C++
ISBN: 189311595X
EAN: 2147483647
Year: 2001
Pages: 127

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net