[Ecls-list] CLOS streams

Juan Jose Garcia-Ripoll worm at arrakis.es
Fri Jun 27 01:59:07 UTC 2003

On Friday 27 June 2003 00:44, Julian St. wrote:
> Hello,
> having the ability to create own streams proves to be really useful for
> using ECL as a plugin. But I wonder what the right way to this it
> (especially what methods I have to provide, currently I just grep'ed
> thorugh the ECL sources to see where ECL_CLOS_STREAMS takes effect). I
> tried it the following way ( %print is the only function that gets called.
> It emits a single line and changes to the next ).
> The implementation as it is seen below works despite of two issues:
> 1) I get the following warnings:
> ;;; Warning: STREAM-CLOSE is being redefined.
> ;;; Warning: STREAM-INPUT-P is being redefined.
> ;;; Warning: STREAM-OUTPUT-P is being redefined.
> ;;; Warning: STREAM-WRITE-CHAR is being redefined.
> ;;; Warning: STREAM-FORCE-OUTPUT is being redefined.
> ;;; Warning: STREAM-CLEAR-OUTPUT is being redefined.

No problem. It just complains because, when you are defining these functions, 
the Common-Lisp package is locked. If you do (SI:PACKAGE-LOCK "CL" NIL)
and (SI:PACKAGE-LOCK "CL" T) before and after your code, everything should 
become quieter.

> 2) I think there too many empty lines written. Looks strange sometimes:

a) Some characters have special meaning in the output. In particular, your 
%print function has to interpret #\Newline not only as a flush character, but 
also as a new line command. See the code I have attached: my implementation 
of %PRINT issues a FRESH-LINE whenever it finds #\Newline, and otherwise 
outputs the character directly to the terminal. Depending on the device which 
you use for output, you may need or not care about this.

b) STREAM-FORCE-OUTPUT may be called any number of times by the lisp 
environement, to ensure that buffers are being flushed, but it does not imply 
that a new line has to be written. Therefore, your STREAM-FLUSH-BUFFER should 
do nothing when the buffer is empty.

c) You do not need to detect #\Newline characters in the STREAM-WRITE-CHAR 
routine. This can be done in the %PRINT-ROUTINE. This way, since your routine 
will operate on strings, the check may be compiled faster. Or, if you use C 
routines to output data, you do not even need to care about the #\Newline.

d) The number of empty lines may differ between a normal stream and a CLOS 
stream, because the FILE-COLUMN functionality has not been implemented for 
the latter, and sometimes the printer has no clue about whether it should 
break a line or not.

BTW, I am going to build an "examples" directory for ECL. Could I include a 
modified version of the file you have posted to the list?


-------------- next part --------------
;; Getting *standard-output* redirected to X-Chat.

;; Complicated version, which shows what your plugin should do
;; when you handle low-level output (for instance X-windows)
(defun %print (s)
  (dotimes (i (length s))
    (let ((c (char s i)))
      (if (char= c #\Newline)
	(fresh-line *terminal-io*)
	(write-char c *terminal-io*)))))

;; Easy version, which relies on the Unix terminal to interpret
;; the #\Newline character.
(defun %print (s)
  (si::write-bytes *terminal-io* s 0 (fill-pointer s)))

(defclass xchat-stream
  ((lbuffer :accessor xchat-stream-buffer
            :initform (make-array  128 :adjustable t :fill-pointer 0
                                   :element-type 'character)))
  (:documentation "A stream directed to X-Chat"))

(defmethod stream-close ((stream xchat-stream))
  (error "Cannot close ~S." stream))

(defmethod stream-input-p ((stream xchat-stream))

(defmethod stream-output-p ((stream xchat-stream))

(defmethod stream-flush-buffer ((stream xchat-stream))
  (let ((buffer (xchat-stream-buffer stream)))
    (unless (zerop (length buffer))
      (%print buffer)
      (stream-clear-output stream))))

(defmethod stream-write-char ((stream xchat-stream) char)
  (let ((buffer (xchat-stream-buffer stream)))
    (unless (vector-push char buffer)
      ;; When VECTOR-PUSH fails, the buffer is full
      (stream-flush-buffer stream)
      (vector-push char buffer))))

(defmethod stream-force-output ((stream xchat-stream))
  (stream-flush-buffer stream))

(defmethod stream-clear-output ((stream xchat-stream))
  (setf (fill-pointer (xchat-stream-buffer stream)) 0))

(setq *new-standard-output* (make-instance 'xchat-stream))

(let ((*standard-output* *new-standard-output*))
  (describe 'CONS)
  (stream-force-output *standard-output*))

More information about the ecl-devel mailing list