[trivial-gray-streams-devel] trivial-gray-stream-mixin in cl+ssl
Anton Vodonosov
avodonosov at yandex.ru
Wed Mar 13 11:01:58 UTC 2013
10.03.2013, 00:10, "James M. Lawrence" <llmjjmll at gmail.com>:
> Hello, I noticed that cl+ssl has the same issue that I recently had
> with gray streams.
>
> (defclass my-stream
> (trivial-gray-streams:fundamental-binary-input-stream
> trivial-gray-streams:fundamental-binary-output-stream
> trivial-gray-streams:trivial-gray-stream-mixin)
> ())
>
> (defmethod trivial-gray-streams:stream-write-byte
> ((stream my-stream) byte)
> (format t "~&write-byte ~s~%" byte))
>
> (defmethod trivial-gray-streams:stream-write-sequence
> ((stream my-stream) seq start end &key)
> (format t "~&write-sequence ~s ~s ~s~%" seq start end))
>
> (defun test ()
> (write-sequence #(3 4 5) (make-instance 'my-stream)))
>
> CL-USER> (test)
> write-byte 3
> write-byte 4
> write-byte 5
>
> The mixin should be the first superclass. With that change, we get
>
> CL-USER> (test)
> write-sequence #(3 4 5) 0 3
>
> Best,
> James
I am fixing that in trivial-gray-streams.
First, let me describe the cause of the problem.
One thing to know is that tirival-gray-streams classes are just reexported from
implementation specific package. For example
trivial-gray-streams:fundamental-stream is EQ to sb-gray:fundamental-stream.
Next, stream-write-sequence is absent in Gray streams proposal (because write-sequence is
absent in CLTL2 I think). Lips implementations provide similar functions, like
sb-gray:stream-write-sequence on SBCL,
gray:stream-write-char-sequence, gray:stream-write-byte-sequence on CLISP
or ccl:stream-write-vector on CCL.
Trivial-gray-streams defines a method for the implementation specific write-sequence analogue function,
and from this method calls trivial-gray-streams:write-sequence.
Besides of the method defined by trivial-gray-streams, the lisp implementation provides
a default method. So, there are two methods for implementation specific write-sequence
analogue:
For example on SBCL:
;; SBCL's default method:
(defmethod sb-gray:stream-write-sequence ((stream fundamental-binary-output-stream) (seq sequence) &optional (start 0) (end nil))
;; trivial gray streams
(defmethod sb-gray:stream-write-sequence ((s trivial-gray-stream-mixin) seq &optional start end)
The class precedence-list for the my-stream class defined above (on SBCL):
COMMON-LISP-USER::MY-STREAM,
SB-GRAY:FUNDAMENTAL-BINARY-INPUT-STREAM,
SB-GRAY:FUNDAMENTAL-INPUT-STREAM,
SB-GRAY:FUNDAMENTAL-BINARY-OUTPUT-STREAM,
SB-GRAY:FUNDAMENTAL-OUTPUT-STREAM,
SB-GRAY:FUNDAMENTAL-BINARY-STREAM,
SB-GRAY:FUNDAMENTAL-STREAM,
TRIVIAL-GRAY-STREAMS:TRIVIAL-GRAY-STREAM-MIXIN,
COMMON-LISP:STANDARD-OBJECT,
SB-PCL::SLOT-OBJECT, COMMON-LISP:STREAM,
COMMON-LISP:T
As you can see, with this class precedence list the SBCL's default method
is choosen (this method calls write-byte repeatedly, as we see in the above test)
If we put mixin first in defclass:
(defclass my-stream
(trivial-gray-streams:fundamental-binary-input-stream
trivial-gray-streams:fundamental-binary-output-stream
trivial-gray-streams:trivial-gray-stream-mixin)
())
then the class precedence list becomes:
COMMON-LISP-USER::MY-STREAM,
TRIVIAL-GRAY-STREAMS:TRIVIAL-GRAY-STREAM-MIXIN,
SB-GRAY:FUNDAMENTAL-BINARY-INPUT-STREAM,
SB-GRAY:FUNDAMENTAL-INPUT-STREAM,
SB-GRAY:FUNDAMENTAL-BINARY-OUTPUT-STREAM,
SB-GRAY:FUNDAMENTAL-OUTPUT-STREAM,
SB-GRAY:FUNDAMENTAL-BINARY-STREAM,
SB-GRAY:FUNDAMENTAL-STREAM,
COMMON-LISP:STANDARD-OBJECT,
SB-PCL::SLOT-OBJECT, COMMON-LISP:STREAM,
COMMON-LISP:T
With this class precedence list the trivial-gray-streams method is chosen.
Summary: in presence of implementation-specific default methods specialized to
sb-gray:* classes, and when user inherits his stream from sb-gray:* classes,
trivial-gray-streams can not ensure that its method is called, but not the default method.
The solution I chosen is that tirival-gray-streams package does not re-export
implementation specific gray classes, but defines and exports its own gray class hierarchy.
Each class in this hierarchy is also inherited from corresponding implementation specific class.
(defclass fundamental-stream (impl-specific-gray:fundamental-stream) ())
(defclass fundamental-input-stream
(fundamental-stream impl-specific-gray:fundamental-input-stream) ())
(defclass fundamental-output-stream
(fundamental-stream impl-specific-gray:fundamental-output-stream) ())
... and so on
That way any user defined class has trivial-gray-stream class in the precedence list
going before the corresponding implementation specific class, and so trivial-gray-streams
methods are always chosen by method dispatch.
This solution makes trivial-gray-stream-mixin class unnecessary.
I have already implemented this in the dev branch of trivial-gray-streams:
https://gitorious.org/trivial-gray-streams/trivial-gray-streams/trees/dev
Also added test-suite. Results seem to be the same as with old version of trivial-gray-streams.
Now I am running cl-test-grid tests of the whole Quicklisp. If there will be no regressions,
I will merge the dev branch to master.
New trivial-gray-stream test suite is not 100% exhaustive, but it's a beginning.
It already shows that all the of the implementations I tested have minor bugs.
One ticket is already fixed by SBCL: https://bugs.launchpad.net/sbcl/+bug/1153257
More tickets are to be reported.
The most often failure is that no one invokes stream-advance-to-column function
when we call (format my-stream "~10,t").
Here is the mapping with failures for various lisps:
(("sbcl-1.1.0.45-win-x86" ("stream-clear-output" "stream-advance-to-column"))
("sbcl-1.1.0.36.mswinmt.1201-284e340-win-x64" ("stream-clear-output" "stream-advance-to-column"))
("sbcl-1.1.0.36.mswinmt.1201-284e340-win-x86" ("stream-clear-output" "stream-advance-to-column"))
("abcl-1.1.0-fasl39-win-x64" ("stream-line-column" "stream-advance-to-column"))
("clisp-2.49-win-x86" ("stream-start-line-p" "stream-write-string" "stream-terpri" "stream-fresh-line" "stream-advance-to-column"))
("ccl-1.8-f95-win-x86" ("stream-advance-to-column"))
("ccl-1.8-f95-win-x64" ("stream-advance-to-column"))
("ecl-12.12.1-ab933fa5-win-x86-bytecode" ("stream-advance-to-column" "stream-file-position" "setf-stream-file-position"))
("acl-9.0a-win-x86" ("stream-advance-to-column")))
If you see any problems with my solution, please let me know.
Best regards,
- Anton
More information about the trivial-gray-streams-devel
mailing list