C.1 Encapsulating Stream Classes

  • C.1.1 Encapsulating Stream Protocol
  • C.1.2 The "Delegation Problem"
  • encapsulating-stream   [Protocol Class]
              

    The protocol class that corresponds to an encapsulating stream. If you want to create a new class that behaves like an encapsulating stream, it should be a subclass of encapsulating-stream. All instantiable subclasses of encapsulating-stream must obey the encapsulating stream protocol. Members of this class are mutable. [annotate]

    [annotate]

    encapsulating-stream-p  object [Protocol Predicate]
              

    Returns true if object is an encapsulating stream, otherwise returns false. [annotate]

    [annotate]

    :stream   [Initarg]
              

    All encapsulating streams must handle the :stream initarg, which is used to specify the stream to be encapsulated. [annotate]

    [annotate]

    standard-encapsulating-stream   [Class]
              

    This instantiable class provides a standard implementation of an encapsulating stream. [annotate]

    [annotate]

    C.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. [annotate]

    The following generic function must also be implemented for all encapsulating stream classes. [annotate]

    encapsulating-stream-stream  encapsulating-stream [Generic Function]
              

    Returns the stream encapsulated by the encapsulating stream encapsulating-stream. [annotate]

    [annotate]

    C.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. [annotate]

    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. [annotate]

    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. [annotate]

    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. [annotate]

    *original-stream*   [Variable]
              

    This variable is bound by the trampoline methods on encapsulating streams to the encapsulating stream, before the operation is delegated to the underlying, encapsulated stream. [annotate]

    [annotate]