summaryrefslogtreecommitdiff
path: root/reference/C/CONTRIB/SNIP/iostutor.txt
diff options
context:
space:
mode:
Diffstat (limited to 'reference/C/CONTRIB/SNIP/iostutor.txt')
-rwxr-xr-xreference/C/CONTRIB/SNIP/iostutor.txt260
1 files changed, 260 insertions, 0 deletions
diff --git a/reference/C/CONTRIB/SNIP/iostutor.txt b/reference/C/CONTRIB/SNIP/iostutor.txt
new file mode 100755
index 0000000..98ca27e
--- /dev/null
+++ b/reference/C/CONTRIB/SNIP/iostutor.txt
@@ -0,0 +1,260 @@
+PROLOGUE --------
+
+This tutorial started out as a "small" example of how to interface an I/O
+object into the AT&T iostream classes currently provided with almost every
+C++ compiler. When I say "small", I mean just that - but the scope of the
+information I wanted to present really didn't fit well into that little hole,
+so it turned out a little larger than I had initially hoped. However, I think
+the effort was worth it - I know it was for me, since although I've done code
+which deals with ostream, I hadn't fooled at all with istream derived
+facilities, and learned some new things in the process.
+
+I can't really cite any single reference on iostreams that might prove useful
+for those wanting to go further other than the AT&T iostreams reference
+manual (my copy is out on loan at the moment so I can't quote the ISBN
+number). I have never come across one which is more complete than this
+reference, and most other references (including documentation from MSC/C++C,
+Borland, IBM etc.) tend to quote from it fairly liberally, but lack any of
+the important information needed to put it all together. While Microsoft's
+C++ tutorial reference has some great hints for getting started in iostream
+manipulators, it lacks in providing any information on interfacing to the
+iostream classes themselves.
+
+Hopefully the information I've provided below will make some sense. It is as
+complete as I could make it without really going overboard. Most of the
+ostream related code is gleaned from my own library, but the rest is brand
+new.
+
+The text of this tutorial and the accompanying source code are donated to the
+public domain.
+
+
+Tutorial in iostreams ---------------------
+
+This little project started out with the following aims:
+
+A) To define a simple input/output object, one that consumes bytes sent to
+ it and generates data for input into a sample program. It should there-
+ fore feature a "read" and "write" function. The object created is simply
+ a loopback buffer - input is queued immediately for output in FIFO (first
+ in first out) fashion.
+
+B) To create a streams interface for this object, so that existing facili-
+ ties for I/O in the AT&T streams classes can be used directly in a
+ polymorphic fashion (ie. make use of C++ inheritance & virtual dispatch).
+
+ Specifically, I wanted to demonstrate the following aspects of iostreams:
+
+ - Use of buffered vs. unbuffered I/O
+
+ - Using the streams buffer for both input and output operations. (an
+ interface to iostream, rather than just istream or ostream)
+
+ - How the put, get and putback buffers work
+
+C) To write a trivial application which uses both components and provide a
+ means of interactively demonstrating how it all works.
+
+
+A - The I/O Object ----------------
+
+class Myio;
+
+This class is simply a front-end for a circular buffer. Input bytes are added
+at the head, and read from the tail. It is fixed in size, set by the
+constructor.
+
+Two read/write functions are provided to access any contained data:
+
+ int Myio::read (char * buf, int max);
+ int Myio::write (char const * buf, int len);
+
+These are both non-blocking calls which return the number of bytes read or
+written. They know nothing about line delineation - only about raw bytes, as
+would be the case for almost any I/O device.
+
+In addition, an internal flag is maintained to indicate when a write results
+in a buffer 'overflow' (an attempt to write more bytes than will fit in the
+buffer) and 'underflow' (an attempt to read an empty buffer). These flags
+reflect the last write and read calls respectively, and are reset or set on
+each write or read call. The members Myio::readok() and Myio::writeok()
+rturn the settings as a boolean value.
+
+A Myio object can also optionally create a stream. It is created and comes
+into life when the member function Myio::stream() is called. If it was
+previously created, this function simply returns a reference to the existing
+stream. The stream, if it exists, is deleted by the destructor.
+
+Myio's stream is an iostream, which inherits all of the abilities of both
+ostream (for output) and istream (for input), including all operators. This,
+of course, is the primary benefit of using streams!
+
+
+B - The Streams Interface -----------------------
+
+class Mystreambuf; class Mystreambase; class Mystream;
+
+Three classes as above are used. Mystreambuf derives from streambuf, and is
+responsible for the input output operations and buffering on behalf of a Myio
+object. Mystreambase is used as a base class for Mystream to assist in the
+initialisation of the (My)streambuf passed to the iostream constructor.
+
+The iostream side is in fact very simple. Nothing really needs to be
+overridden, and all of the work is done in Mystreambuf, where all the action
+really takes place.
+
+The relationship between the ios/stream classes and the streambuf family is
+one of delegation rather than inheritance. The user/application accesses
+streambuf I/O via the stream object, not directly. A class diagram showing
+the basic iostream classes and our classes would look like:
+
+
+ _ istream _ / \ ios -- ostream -- iostream \ \ \ Mystream \_____
+Mystreambase _/ | | (owns) | streambuf -- Mystreambuf
+
+
+All relationships, except the one marked "(owns)", indicate inheritance. The
+'owns' relationship is where the delegation occurs. ios is inherited
+virtually, so that there is only one combined 'ios' object at the root of the
+streams inheritence tree.
+
+Within Mystreambuf, we need to override the functions responsible for actual
+input and output. But first, let's discuss how this streambuf works.
+
+Mystreambuf uses a single buffer, using the default buffer size allocated for
+any streambuf (under most operating systems, this will be 1024 bytes). Since
+we are dealing with both input and output operations, and these operations
+are independent so far as the streambuf is concerned (as is the case with,
+for example, serial I/O, but *not* the case with files), the buffer is split
+into two; the first half is used for writing, the second for reading.
+
+The buffer used for writing is called the "put" buffer. Nothing mysterious
+there - when full, streambuf::overflow() function is called, and via virtual
+dispatch calls our Mystreambuf::overflow() which takes the contents of the
+buffer and writes it to the output device.
+
+The read - or "get" - buffer is slightly more complex. There are occasions in
+dealing with an input stream where it is convenient to know what's next
+without actually removing it from the stream. That way, you can use the next
+character as an indication of what to do next. For example, if you're parsing
+a number, you want to know whether or not the next character is a valid
+digit, and stop parsing if it isn't. The read side therefore incorporates the
+idea of a "putback" buffer - after being read, the character can be placed
+back into the input stream.
+
+The putback buffer is entirely the responsibility of any streambuf derived
+class. It most you need to support a one character putback buffer - it is not
+valid to remove, and then restore, more than one character from the stream.
+It is also not valid put 'putback' any character but the one that was the
+result of the last 'get'. It really must be "put back", not any old
+character "pushed" (you could actually support 'pushing' data into the stream
+if you wanted to, but you shouldn't use putback to do it).
+
+The get buffer is set up as:
+
+ Offset 0 1 2 3 .... n | | | | to end of buffer |
++---+---+---+---------------------+ ^ ^ | +- Start of get buffer (where data
+is read in) | +- Where data is putback
+
+Each time streambuf runs out of characters to give to its client, the
+underflow() function is called. This fills the get buffer (get buffer size -
+1, starting at offset 1) and reserves the first byte in case the previously
+read character needs to be put back.
+
+streambuf provides internal pointers into the put, get and putback areas. All
+of the I/O functions it provides handle these automatically. In our
+underflow() and overflow() functions, we need to set these pointers according
+to where and how much data is read in.
+
+I mentioned above that in our case, the input & output streams are
+independant. That's not entirely the case - it may happen that when reading
+from the Myio buffer we run out of data and need additional data in the
+output stream buffer not yet written to Myio. We therefore flush the output
+stream before retrieving any data by calling overflow() directly from within
+underflow().
+
+The sync() function is also overridden. This simply empties all buffers by
+flushing the output buffer and discarding any buffered input.
+
+
+C - The Application -----------------
+
+The application itself is a simple menu, offering choice to send a line of
+output to the IO object (via its stream), read one in, and dump/display
+information both about the stream and Myio object.
+
+This added two other classes to the project:
+
+ - myApplication: the actual application, implemented as a class. The
+ only way to go in C++. :-)
+
+ - myList: a simple line input class, whose sole purpose in life is to
+ extract a linefeed delimited line from any istream object and return it
+ as a char const *. (I posted this code last week, but have since fixed
+ one minor bug I found in the process of developing Myio).
+
+A couple of subtle points - class myApplication uses a pointer to member
+function it its menu selection. This is not the only way of doing this of
+course, but I thought it was a good way of demonstrating a very C++ specific
+concept, operator ->*, which does not exist at all in C.
+
+Additional notes are included in the source comments.
+
+
+Making the application ----------------------
+
+Hopefully this is a fairly simple thing to do - just compile the modules:
+
+ Myiodemo.cpp Myio.cpp Mystream.cpp myLine.cpp
+
+and link them together. A simple makefile is provided - take a look at the
+definitions at the top, adjust as desired, and type "make" (or nmake). If you
+use any of Borland's compilers, just add the above files to a new project
+called "Myiodemo.PRJ", set it to produce a .EXE (*not* Windows or PM based)
+and press F9.
+
+Assuming a C++ compiler compatibile with cfront 2.1 and the presence of an
+iostreans 1.2 library, the only non-portable part of this app is the use of
+getch() from conio.h. This isn't easily provided under a UNIX system. You can
+either fudge it by writing a getch() which switches into/out of 'raw' mode,
+or use getchar() and clear everything up to and including a CR or NL after
+the first character (the user still has to hit CR for input to get to the
+program).
+
+
+
+EPILOGUE --------
+
+Just some notes as to use of this code. If you need an output or input only
+class, then you use ostream or istream wherever iostream is mentioned in this
+example. Also, if you use buffered mode (you can support it or not - you can
+even ignore the streambuf setting at your discretion), then you can use the
+entire buffer rather than just half each for input output.
+
+If you interface to an input only object, you only need to override
+streambuf::underflow(). Conversely, you override streambuf::overflow() for an
+output only object. I have noticed that *some* implementations of iostreams
+define the overflow() and underflow() methods as pure virtual functions,
+whereas the AT&T default defines each as simply returning EOF.
+
+If portability is any concern, you may need to override the function you
+aren't using in this fashion. The default sync() simply returns 0 (success),
+but again, this is sometimes defined as a pure virtual, so you may need to
+define it in your implementation.
+
+In some cases, you may wish to "switch" between unbuffered and buffered
+modes. This is easily done by defining a function in Mystream which does it,
+and this object is of course accessible in your I/O object (in this case
+Myio). The only thing you need to remember is to flush all the buffers by
+calling sync() when switching from buffered to unbuffered mode.
+
+Note also that some streambuf constructors take an existing buffer. This
+means that you can use buffers already provided in your I/O object directly
+rather than being forced to "double buffer" anything. Your buffer can also
+be any size you like, subject to memory and other architecture constraints.
+
+
+Enjoy!
+
+David Nugent - 3:632/348@fidonet.org
+Moderor ('93-'94) of the FidoNet international C++ EchoMail conference