| The CLIM-SYS Package | Contents | Index | Common Lisp Streams |
33 Encapsulating Streams
An encapsulating stream is a special kind of stream that ``closes
over'' another stream, handling some of the usual stream protocol operations
itself, and delegating the remaining operations to the ``encapsulated'' stream.
Encapsulating streams may be used by some CLIM implementations in order to
facilitate the implementation of features that require the dynamic modification
of a stream's state and operations. For example, accepting-values dialogs
can be implemented by using an encapsulating stream that tailors calls to
accept and prompt-for-accept in such a way that the output is captured
and formatted into a dialog that contains prompts and fields that can be clicked
on and modified by the user. Input editing can also be implemented using an
encapsulating stream that manages the interaction between read-gesture and
the input editing commands and rescanning. The form filling-output can be
implemented by having an encapsulating stream that buffers output and inserts
line breaks appropriately.
CLIM implementations need not use encapsulating streams at all. If encapsulating streams are used, they must adhere to the following protocols. Encapsulating streams are not part of CLIM's API.
33.1 Encapsulating Stream Classes
| encapsulating-stream | [Protocol Class] |
| encapsulating-stream-p | object | [Predicate] |
| :stream | [Init arg] |
| standard-encapsulating-stream | [Class] |
33.1.1 Encapsulating Stream Protocol
The standard-encapsulating-stream class must provide ``trampoline'' methods
for all stream protocol operations. These ``trampolines'' will simply
call the same generic function on the encapsulated stream. In particular, all
of the generic functions in the following protocols must have trampolines.
| encapsulating-stream-stream | encapsulating-stream | [Generic function] |
33.1.2 The ``Delegation Problem''
The suggested implementation of encapsulating streams has a potential problem
that we label the ``delegation'' or ``multiple self'' problem. Here is an
example of the problem.
Suppose we implement accepting-values by using an encapsulating stream class called accepting-values-stream that will be used to close over an ordinary extended input and output stream. Let us examine two generic functions, stream-accept and prompt-for-accept . The stream-accept method on an ordinary stream calls prompt-for-accept . Now suppose that accepting-values-stream specializes prompt-for-accept . If we now create a stream of type accepting-values-stream (which we will designate A) which encapsulates an ordinary stream S, and then call stream-accept on the stream E, it will trampoline to stream-accept on the stream S. The desired behavior is for stream-accept to call the prompt-for-accept method on the stream E, but instead what happens is that the prompt-for-accept method on the stream S is called.
In order to side-step this problem without attempting to solve a difficult general problem in object-oriented programming, CLIM implementations may introduce a special variable, *original-stream* , which is bound by trampoline functions to the original encapsulating stream. Therefore, the stream-accept on the ordinary stream S will call prompt-for-accept on the value of (or *original-stream* stream ) . This idiom only needs to be used in places where one stream protocol function calls a second stream protocol function that some encapsulating stream specializes.
This ``solution'' does not solve the more general problem of multiple levels of encapsulation, but the complete stream protocol provided by CLIM should allow implementors to avoid using nested encapsulating streams.
| *original-stream* | [Variable] |
| The CLIM-SYS Package | Contents | Index | Common Lisp Streams |