mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
579 lines
25 KiB
Plaintext
579 lines
25 KiB
Plaintext
@c Generated by the sb-manual contrib. Do not edit.
|
|
|
|
@node streams
|
|
@chapter Streams
|
|
|
|
@menu
|
|
* Stream External Formats: stream external formats.
|
|
* Bivalent Streams: bivalent streams.
|
|
* Gray Streams: gray streams.
|
|
* Simple Streams: sb simple streams.
|
|
@end menu
|
|
|
|
Streams which read or write Lisp character data from or to the outside
|
|
world -- files, sockets or other external entities -- require the
|
|
specification of a conversion between the external, binary data and
|
|
the Lisp characters. In ANSI Common Lisp, this is done by specifying
|
|
the @code{:external-format} argument when the stream is created. The major
|
|
information required is an @emph{encoding}, specified by a keyword naming
|
|
that encoding; however, it is also possible to specify refinements
|
|
to that encoding as additional options to the external format
|
|
designator.
|
|
|
|
In addition, SBCL supports various extensions of ANSI Common Lisp
|
|
streams:
|
|
|
|
@itemize
|
|
@item @emph{Bivalent Streams}: A type of stream that can read and write both
|
|
@code{character} and @code{(unsigned-byte 8)} values.
|
|
|
|
@item @emph{Gray Streams}: User-overloadable CLOS classes whose instances can
|
|
be used as Lisp streams (e.g. passed as the first argument to
|
|
@code{format}).
|
|
|
|
@item @emph{Simple Streams}: The bundled contrib module @code{sb-simple-streams}
|
|
implements a subset of the Franz Allegro simple-streams proposal.
|
|
@end itemize
|
|
|
|
@node stream external formats
|
|
@section Stream External Formats
|
|
|
|
@cindex external format
|
|
@cindex format, external
|
|
The function @code{stream-external-format} returns the canonical name of
|
|
the external format (see @ref{external formats}) used by the stream for
|
|
character-based input and/or output.
|
|
|
|
When constructing file streams, for example using @code{open} or
|
|
@code{with-open-file}, the external format to use is specified via the
|
|
@code{:external-format} argument which accepts an external format
|
|
designator (see @ref{external format designators}).
|
|
|
|
@node bivalent streams
|
|
@section Bivalent Streams
|
|
|
|
A @emph{bivalent stream} can be used to read and write both
|
|
@code{character} and @code{(unsigned-byte 8)} values. A bivalent stream is
|
|
created by calling @code{open} with the argument @code{:element-type}
|
|
@code{:default}. On such a stream, both binary and character data can be
|
|
read and written with the usual input and output functions.
|
|
|
|
Streams are @emph{not} created bivalent by default for performance
|
|
reasons. Bivalent streams are incompatible with @code{fast-read-char}, an
|
|
internal optimization in SBCL's stream machinery that bulk-converts
|
|
octets to characters and implements a fast path through @code{read-char}.
|
|
|
|
@node gray streams
|
|
@section Gray Streams
|
|
|
|
@menu
|
|
* Gray Streams classes: gray streams classes.
|
|
* Methods common to all streams: methods common to all streams.
|
|
* Input stream methods: input stream methods.
|
|
* Character input stream methods: character input stream methods.
|
|
* Output stream methods: output stream methods.
|
|
* Character output stream methods: character output stream methods.
|
|
* Binary stream methods: binary stream methods.
|
|
* Gray Streams Examples: gray streams examples.
|
|
@end menu
|
|
|
|
The Gray Streams interface is a widely supported extension that
|
|
provides for definition of CLOS-extensible stream classes. Gray
|
|
stream classes are implemented by adding methods to generic
|
|
functions analogous to Common Lisp's standard I/O functions.
|
|
Instances of Gray stream classes may be used with any I/O operation
|
|
where a non-Gray stream can, provided that all required methods have
|
|
been implemented suitably.
|
|
|
|
@node gray streams classes
|
|
@subsection Gray Streams classes
|
|
|
|
The defined Gray Stream classes are these:
|
|
|
|
@anchor{Class sb-gray fundamental-stream}
|
|
@ttindex @sortas{fundamental-stream sb-gray} fundamental-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-stream
|
|
Base class for all Gray streams.
|
|
@end deffn
|
|
@anchor{Class sb-gray fundamental-input-stream}
|
|
@ttindex @sortas{fundamental-input-stream sb-gray} fundamental-input-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-input-stream
|
|
Superclass of all Gray input streams.
|
|
@end deffn
|
|
The function @code{input-stream-p} will return true of any generalized
|
|
instance of @code{sb-gray:fundamental-input-stream}.
|
|
|
|
@anchor{Class sb-gray fundamental-output-stream}
|
|
@ttindex @sortas{fundamental-output-stream sb-gray} fundamental-output-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-output-stream
|
|
Superclass of all Gray output streams.
|
|
@end deffn
|
|
The function @code{output-stream-p} will return true of any generalized
|
|
instance of @code{sb-gray:fundamental-output-stream}.
|
|
|
|
@anchor{Class sb-gray fundamental-binary-stream}
|
|
@ttindex @sortas{fundamental-binary-stream sb-gray} fundamental-binary-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-binary-stream
|
|
Superclass of all Gray streams whose element-type
|
|
is a subtype of unsigned-byte or signed-byte.
|
|
@end deffn
|
|
Note that instantiable subclasses of @code{sb-gray:fundamental-binary-stream}
|
|
should provide (or inherit) an applicable method for the generic
|
|
function @code{stream-element-type}.
|
|
|
|
@anchor{Class sb-gray fundamental-character-stream}
|
|
@ttindex @sortas{fundamental-character-stream sb-gray} fundamental-character-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-character-stream
|
|
Superclass of all Gray streams whose element-type is a subtype of character.
|
|
@end deffn
|
|
@anchor{Class sb-gray fundamental-binary-input-stream}
|
|
@ttindex @sortas{fundamental-binary-input-stream sb-gray} fundamental-binary-input-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-binary-input-stream
|
|
Superclass of all Gray input streams whose element-type
|
|
is a subtype of unsigned-byte or signed-byte.
|
|
@end deffn
|
|
@anchor{Class sb-gray fundamental-binary-output-stream}
|
|
@ttindex @sortas{fundamental-binary-output-stream sb-gray} fundamental-binary-output-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-binary-output-stream
|
|
Superclass of all Gray output streams whose element-type
|
|
is a subtype of unsigned-byte or signed-byte.
|
|
@end deffn
|
|
@anchor{Class sb-gray fundamental-character-input-stream}
|
|
@ttindex @sortas{fundamental-character-input-stream sb-gray} fundamental-character-input-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-character-input-stream
|
|
Superclass of all Gray input streams whose element-type
|
|
is a subtype of character.
|
|
@end deffn
|
|
@anchor{Class sb-gray fundamental-character-output-stream}
|
|
@ttindex @sortas{fundamental-character-output-stream sb-gray} fundamental-character-output-stream [sb-gray]
|
|
@deffn{Class} sb-gray:fundamental-character-output-stream
|
|
Superclass of all Gray output streams whose element-type
|
|
is a subtype of character.
|
|
@end deffn
|
|
@node methods common to all streams
|
|
@subsection Methods common to all streams
|
|
|
|
These generic functions can be specialized on any generalized instance
|
|
of fundamental-stream.
|
|
|
|
@anchor{Generic function common-lisp stream-element-type}
|
|
@ffindex @sortas{stream-element-type common-lisp} stream-element-type [common-lisp]
|
|
@deffn{Generic function} stream-element-type stream
|
|
Return a type specifier for the kind of object returned by the
|
|
@code{stream}. The class @code{sb-gray:fundamental-character-stream} provides a
|
|
default method which returns @code{character}.
|
|
@end deffn
|
|
@anchor{Generic function common-lisp close}
|
|
@ffindex @sortas{close common-lisp} close [common-lisp]
|
|
@deffn{Generic function} close stream &key abort
|
|
Close the given @code{stream}. No more I/O may be performed, but
|
|
inquiries may still be made. If @code{:abort} is true, an attempt is made
|
|
to clean up the side effects of having created the stream.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-file-position}
|
|
@ffindex @sortas{stream-file-position sb-gray} stream-file-position [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-file-position stream &optional position-spec
|
|
Used by @code{file-position}. Returns or changes the current position within @code{stream}.
|
|
@end deffn
|
|
@node input stream methods
|
|
@subsection Input stream methods
|
|
|
|
These generic functions may be specialized on any generalized instance
|
|
of fundamental-input-stream.
|
|
|
|
@anchor{Generic function sb-gray stream-clear-input}
|
|
@ffindex @sortas{stream-clear-input sb-gray} stream-clear-input [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-clear-input stream
|
|
This is like @code{cl:clear-input}, but for Gray streams, returning @code{nil}.
|
|
The default method does nothing.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-read-sequence}
|
|
@ffindex @sortas{stream-read-sequence sb-gray} stream-read-sequence [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-read-sequence stream seq &optional start end
|
|
This is like @code{cl:read-sequence}, but for Gray streams.
|
|
@end deffn
|
|
@node character input stream methods
|
|
@subsection Character input stream methods
|
|
|
|
These generic functions are used to implement subclasses of
|
|
@code{sb-gray:fundamental-input-stream}:
|
|
|
|
@anchor{Generic function sb-gray stream-peek-char}
|
|
@ffindex @sortas{stream-peek-char sb-gray} stream-peek-char [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-peek-char stream
|
|
This is used to implement @code{peek-char}; this corresponds to @code{peek-type}
|
|
of @code{nil}. It returns either a character or @code{:eof}. The default method
|
|
calls @code{stream-read-char} and @code{stream-unread-char}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-read-char-no-hang}
|
|
@ffindex @sortas{stream-read-char-no-hang sb-gray} stream-read-char-no-hang [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-read-char-no-hang stream
|
|
This is used to implement @code{read-char-no-hang}. It returns either a
|
|
character, or @code{nil} if no input is currently available, or @code{:eof} if
|
|
end-of-file is reached. The default method provided by
|
|
@code{fundamental-character-input-stream} simply calls @code{stream-read-char}; this
|
|
is sufficient for file streams, but interactive streams should define
|
|
their own method.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-read-char}
|
|
@ffindex @sortas{stream-read-char sb-gray} stream-read-char [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-read-char stream
|
|
Read one character from the stream. Return either a
|
|
character object, or the symbol @code{:eof} if the stream is at end-of-file.
|
|
Every subclass of @code{fundamental-character-input-stream} must define a
|
|
method for this function.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-read-line}
|
|
@ffindex @sortas{stream-read-line sb-gray} stream-read-line [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-read-line stream
|
|
This is used by @code{read-line}. A string is returned as the first value. The
|
|
second value is true if the string was terminated by end-of-file
|
|
instead of the end of a line. The default method uses repeated
|
|
calls to @code{stream-read-char}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-listen}
|
|
@ffindex @sortas{stream-listen sb-gray} stream-listen [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-listen stream
|
|
This is used by @code{listen}. It returns true or false. The default method uses
|
|
@code{stream-read-char-no-hang} and @code{stream-unread-char}. Most streams should
|
|
define their own method since it will usually be trivial and will
|
|
always be more efficient than the default method.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-unread-char}
|
|
@ffindex @sortas{stream-unread-char sb-gray} stream-unread-char [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-unread-char stream character
|
|
Undo the last call to @code{stream-read-char}, as in @code{unread-char}.
|
|
Return @code{nil}. Every subclass of @code{fundamental-character-input-stream}
|
|
must define a method for this function.
|
|
@end deffn
|
|
@node output stream methods
|
|
@subsection Output stream methods
|
|
|
|
These generic functions are used to implement subclasses of
|
|
@code{sb-gray:fundamental-output-stream}:
|
|
|
|
@anchor{Generic function sb-gray stream-clear-output}
|
|
@ffindex @sortas{stream-clear-output sb-gray} stream-clear-output [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-clear-output stream
|
|
This is like @code{cl:clear-output}, but for Gray streams: clear the given
|
|
output @code{stream}. The default method does nothing.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-finish-output}
|
|
@ffindex @sortas{stream-finish-output sb-gray} stream-finish-output [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-finish-output stream
|
|
Attempts to ensure that all output sent to the Stream has reached
|
|
its destination, and only then returns false. Implements
|
|
@code{finish-output}. The default method does nothing.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-force-output}
|
|
@ffindex @sortas{stream-force-output sb-gray} stream-force-output [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-force-output stream
|
|
Attempts to force any buffered output to be sent. Implements
|
|
@code{force-output}. The default method does nothing.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-write-sequence}
|
|
@ffindex @sortas{stream-write-sequence sb-gray} stream-write-sequence [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-write-sequence stream seq &optional start end
|
|
This is like @code{cl:write-sequence}, but for Gray streams.
|
|
@end deffn
|
|
@node character output stream methods
|
|
@subsection Character output stream methods
|
|
|
|
These generic functions are used to implement subclasses of
|
|
@code{sb-gray:fundamental-character-output-stream}:
|
|
|
|
@anchor{Generic function sb-gray stream-advance-to-column}
|
|
@ffindex @sortas{stream-advance-to-column sb-gray} stream-advance-to-column [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-advance-to-column stream column
|
|
Write enough blank space so that the next character will be
|
|
written at the specified column. Returns true if the operation is
|
|
successful, or @code{nil} if it is not supported for this stream. This is
|
|
intended for use by by @code{pprint} and @code{format} @code{~t}. The default method
|
|
uses @code{stream-line-column} and repeated calls to @code{stream-write-char}
|
|
with a #SPACE character; it returns @code{nil} if @code{stream-line-column}
|
|
returns @code{nil}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-fresh-line}
|
|
@ffindex @sortas{stream-fresh-line sb-gray} stream-fresh-line [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-fresh-line stream
|
|
Outputs a new line to the Stream if it is not positioned at the
|
|
beginning of a line. Returns @code{t} if it output a new line, nil
|
|
otherwise. Used by @code{fresh-line}. The default method uses
|
|
@code{stream-start-line-p} and @code{stream-terpri}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-line-column}
|
|
@ffindex @sortas{stream-line-column sb-gray} stream-line-column [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-line-column stream
|
|
Return the column number where the next character
|
|
will be written, or @code{nil} if that is not meaningful for this stream.
|
|
The first column on a line is numbered 0. This function is used in
|
|
the implementation of @code{pprint} and the @code{format} @code{~t} directive. For every
|
|
character output stream class that is defined, a method must be
|
|
defined for this function, although it is permissible for it to
|
|
always return @code{nil}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-line-length}
|
|
@ffindex @sortas{stream-line-length sb-gray} stream-line-length [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-line-length stream
|
|
Return the stream line length or @code{nil}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-start-line-p}
|
|
@ffindex @sortas{stream-start-line-p sb-gray} stream-start-line-p [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-start-line-p stream
|
|
Is @code{stream} known to be positioned at the beginning of a line?
|
|
It is permissible for an implementation to always return
|
|
@code{nil}. This is used in the implementation of @code{fresh-line}. Note that
|
|
while a value of 0 from @code{stream-line-column} also indicates the
|
|
beginning of a line, there are cases where @code{stream-start-line-p} can be
|
|
meaningfully implemented although @code{stream-line-column} can't be. For
|
|
example, for a window using variable-width characters, the column
|
|
number isn't very meaningful, but the beginning of the line does have
|
|
a clear meaning. The default method for @code{stream-start-line-p} on class
|
|
@code{fundamental-character-output-stream} uses @code{stream-line-column}, so if
|
|
that is defined to return @code{nil}, then a method should be provided for
|
|
either @code{stream-start-line-p} or @code{stream-fresh-line}.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-terpri}
|
|
@ffindex @sortas{stream-terpri sb-gray} stream-terpri [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-terpri stream
|
|
Writes an end of line, as for @code{terpri}. Returns @code{nil}. The default
|
|
method does (@code{stream-write-char} stream @code{#\Newline}).
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-write-char}
|
|
@ffindex @sortas{stream-write-char sb-gray} stream-write-char [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-write-char stream character
|
|
Write @code{character} to @code{stream} and return @code{character}. Every
|
|
subclass of @code{fundamental-character-output-stream} must have a method
|
|
defined for this function.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-write-string}
|
|
@ffindex @sortas{stream-write-string sb-gray} stream-write-string [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-write-string stream string &optional start end
|
|
This is used by @code{write-string}. It writes the string to the stream,
|
|
optionally delimited by start and end, which default to 0 and @code{nil}.
|
|
The string argument is returned. The default method provided by
|
|
@code{fundamental-character-output-stream} uses repeated calls to
|
|
@code{stream-write-char}.
|
|
@end deffn
|
|
@node binary stream methods
|
|
@subsection Binary stream methods
|
|
|
|
The following generic functions are available for subclasses of
|
|
@code{sb-gray:fundamental-binary-stream}:
|
|
|
|
@anchor{Generic function sb-gray stream-read-byte}
|
|
@ffindex @sortas{stream-read-byte sb-gray} stream-read-byte [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-read-byte stream
|
|
Used by @code{read-byte}; returns either an integer, or the symbol @code{:eof}
|
|
if the stream is at end-of-file.
|
|
@end deffn
|
|
@anchor{Generic function sb-gray stream-write-byte}
|
|
@ffindex @sortas{stream-write-byte sb-gray} stream-write-byte [sb-gray]
|
|
@deffn{Generic function} sb-gray:stream-write-byte stream integer
|
|
Implements @code{write-byte}; writes the integer to the stream and
|
|
returns the integer as the result.
|
|
@end deffn
|
|
@node gray streams examples
|
|
@subsection Gray Streams Examples
|
|
|
|
@menu
|
|
* Character Counting Input Stream: character counting input stream.
|
|
* Output Prefixing Character Stream: output prefixing character stream.
|
|
@end menu
|
|
|
|
Below are two classes of stream that can be conveniently defined as
|
|
wrappers for Common Lisp streams. These are meant to serve as
|
|
examples of minimal implementations of the protocols that must be
|
|
followed when defining Gray streams. Realistic uses of the Gray
|
|
Streams API would implement the various methods that can do I/O in
|
|
batches, such as @code{sb-gray:stream-read-line},
|
|
@code{sb-gray:stream-write-string}, @code{sb-gray:stream-read-sequence}, and
|
|
@code{sb-gray:stream-write-sequence}.
|
|
|
|
@node character counting input stream
|
|
@subsubsection Character Counting Input Stream
|
|
|
|
It is occasionally handy for programs that process input files to
|
|
count the number of characters and lines seen so far, and the number
|
|
of characters seen on the current line, so that useful messages may
|
|
be reported in case of parsing errors, etc. Here is a character
|
|
input stream class that keeps track of these counts. Note that all
|
|
character input streams must implement @code{sb-gray:stream-read-char} and
|
|
@code{sb-gray:stream-unread-char}.
|
|
|
|
@example
|
|
(defclass wrapped-stream (fundamental-stream)
|
|
((stream :initarg :stream :reader stream-of)))
|
|
|
|
(defmethod stream-element-type ((stream wrapped-stream))
|
|
(stream-element-type (stream-of stream)))
|
|
|
|
(defmethod close ((stream wrapped-stream) &key abort)
|
|
(close (stream-of stream) :abort abort))
|
|
|
|
(defclass wrapped-character-input-stream
|
|
(wrapped-stream fundamental-character-input-stream)
|
|
())
|
|
|
|
(defmethod stream-read-char ((stream wrapped-character-input-stream))
|
|
(read-char (stream-of stream) nil :eof))
|
|
|
|
(defmethod stream-unread-char ((stream wrapped-character-input-stream)
|
|
char)
|
|
(unread-char char (stream-of stream)))
|
|
|
|
(defclass counting-character-input-stream
|
|
(wrapped-character-input-stream)
|
|
((char-count :initform 1 :accessor char-count-of)
|
|
(line-count :initform 1 :accessor line-count-of)
|
|
(col-count :initform 1 :accessor col-count-of)
|
|
(prev-col-count :initform 1 :accessor prev-col-count-of)))
|
|
|
|
(defmethod stream-read-char ((stream counting-character-input-stream))
|
|
(with-accessors ((inner-stream stream-of) (chars char-count-of)
|
|
(lines line-count-of) (cols col-count-of)
|
|
(prev prev-col-count-of)) stream
|
|
(let ((char (call-next-method)))
|
|
(cond ((eql char :eof)
|
|
:eof)
|
|
((char= char #Newline)
|
|
(incf lines)
|
|
(incf chars)
|
|
(setf prev cols)
|
|
(setf cols 1)
|
|
char)
|
|
(t
|
|
(incf chars)
|
|
(incf cols)
|
|
char)))))
|
|
|
|
(defmethod stream-unread-char ((stream counting-character-input-stream)
|
|
char)
|
|
(with-accessors ((inner-stream stream-of) (chars char-count-of)
|
|
(lines line-count-of) (cols col-count-of)
|
|
(prev prev-col-count-of)) stream
|
|
(cond ((char= char #Newline)
|
|
(decf lines)
|
|
(decf chars)
|
|
(setf cols prev))
|
|
(t
|
|
(decf chars)
|
|
(decf cols)
|
|
char))
|
|
(call-next-method)))
|
|
@end example
|
|
|
|
The default methods for @code{sb-gray:stream-read-char-no-hang},
|
|
@code{sb-gray:stream-peek-char}, @code{sb-gray:stream-listen},
|
|
@code{sb-gray:stream-clear-input}, @code{sb-gray:stream-read-line}, and
|
|
@code{sb-gray:stream-read-sequence} should be sufficient (though the last
|
|
two will probably be slower than methods that forwarded directly).
|
|
|
|
Here's a sample use of this class:
|
|
|
|
@example
|
|
(with-input-from-string (input "1 2
|
|
3 :foo ")
|
|
(let ((counted-stream (make-instance 'counting-character-input-stream
|
|
:stream input)))
|
|
(loop for thing = (read counted-stream) while thing
|
|
unless (numberp thing) do
|
|
(error "Non-number ~S (line ~D, column ~D)" thing
|
|
(line-count-of counted-stream)
|
|
(- (col-count-of counted-stream)
|
|
(length (format nil "~S" thing))))
|
|
end
|
|
do (print thing))))
|
|
@end example
|
|
|
|
Output:
|
|
|
|
@example
|
|
1
|
|
2
|
|
3
|
|
Non-number :FOO (line 2, column 5)
|
|
[Condition of type SIMPLE-ERROR]
|
|
@end example
|
|
|
|
@node output prefixing character stream
|
|
@subsubsection Output Prefixing Character Stream
|
|
|
|
One use for a wrapped output stream might be to prefix each line of
|
|
text with a timestamp, e.g. for a logging stream. Here's a simple
|
|
stream that does this, though without any fancy line-wrapping. Note
|
|
that all character output stream classes must implement
|
|
@code{sb-gray:stream-write-char} and @code{sb-gray:stream-line-column}.
|
|
|
|
@example
|
|
(defclass wrapped-stream (fundamental-stream)
|
|
((stream :initarg :stream :reader stream-of)))
|
|
|
|
(defmethod stream-element-type ((stream wrapped-stream))
|
|
(stream-element-type (stream-of stream)))
|
|
|
|
(defmethod close ((stream wrapped-stream) &key abort)
|
|
(close (stream-of stream) :abort abort))
|
|
|
|
(defclass wrapped-character-output-stream
|
|
(wrapped-stream fundamental-character-output-stream)
|
|
((col-index :initform 0 :accessor col-index-of)))
|
|
|
|
(defmethod stream-line-column ((stream wrapped-character-output-stream))
|
|
(col-index-of stream))
|
|
|
|
(defmethod stream-write-char ((stream wrapped-character-output-stream)
|
|
char)
|
|
(with-accessors ((inner-stream stream-of) (cols col-index-of)) stream
|
|
(write-char char inner-stream)
|
|
(if (char= char #Newline)
|
|
(setf cols 0)
|
|
(incf cols))))
|
|
|
|
(defclass prefixed-character-output-stream
|
|
(wrapped-character-output-stream)
|
|
((prefix :initarg :prefix :reader prefix-of)))
|
|
|
|
(defgeneric write-prefix (prefix stream)
|
|
(:method ((prefix string) stream) (write-string prefix stream))
|
|
(:method ((prefix function) stream) (funcall prefix stream)))
|
|
|
|
(defmethod stream-write-char ((stream prefixed-character-output-stream)
|
|
char)
|
|
(with-accessors ((inner-stream stream-of) (cols col-index-of)
|
|
(prefix prefix-of)) stream
|
|
(when (zerop cols)
|
|
(write-prefix prefix inner-stream))
|
|
(call-next-method)))
|
|
@end example
|
|
|
|
As with the example input stream, this implements only the minimal
|
|
protocol. A production implementation should also provide methods
|
|
for at least @code{sb-gray:stream-write-string},
|
|
@code{sb-gray:stream-write-sequence}.
|
|
|
|
And here's a sample use of this class:
|
|
|
|
@example
|
|
(flet ((format-timestamp (stream)
|
|
(apply #'format stream "[~2@@*~2,' D:~1@@*~2,'0D:~0@@*~2,'0D] "
|
|
(multiple-value-list (get-decoded-time)))))
|
|
(let ((output (make-instance 'prefixed-character-output-stream
|
|
:stream *standard-output*
|
|
:prefix #'format-timestamp)))
|
|
(loop for string in '("abc" "def" ")ghi") do
|
|
(write-line string output)
|
|
(sleep 1))))
|
|
@end example
|
|
|
|
Output:
|
|
|
|
@example
|
|
[ 0:30:05] abc
|
|
[ 0:30:06] def
|
|
[ 0:30:07] ghi
|
|
NIL
|
|
@end example
|
|
|
|
@include ../../contrib/sb-simple-streams/sb-simple-streams.texinfo
|