Open mmontone opened 1 year ago
I've just realized this is equivalent to UIOP/STREAM:WITH-OUTPUT
My version supports passing arguments to OPEN when opening files, and that's important for my use cases, as I may want to overwrite, etc. So this may still be useful.
The one concern I have it here is interpreting strings as files -- cl:with-output-to-string
, serapeum:with-string
, and uiop:with-output
all allow writing to a string with a fill pointer as if it were a stream. The function should at least check for a fill pointer and treat it as a stream in that case.
Ok. I'll improve it
El dom., 23 jul. 2023 11:28, Paul M. Rodriguez @.***> escribió:
The one concern I have it here is interpreting strings as files -- cl:with-output-to-string, serapeum:with-string, and uiop:with-output all allow writing to a string with a fill pointer as if it were a stream. The function should at least check for a fill pointer and treat it as a stream in that case.
— Reply to this email directly, view it on GitHub https://github.com/ruricolist/serapeum/issues/155#issuecomment-1646853656, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADKPDWQN4VDKRTMIZ3QLBLXRUYHHANCNFSM6AAAAAA2UPODMU . You are receiving this because you modified the open/close state.Message ID: @.***>
How about this:
(defun call-with-output-to-destination (destination function &rest args)
"Evaluate FUNCTION with a stream created from DESTINATION as argument.
If DESTINATION is a pathname, then open the file for writing. ARGS are used in the OPEN call.
If it is a string with a fill-pointer, WITH-OUTPUT-TO-STRING is used to create a stream for it.
If it is a stream, then it is used as it is.
If it is NIL, then WITH-OUTPUT-TO-STRING is used to create the stream.
If it is T, then *STANDARD-OUTPUT* is used for the stream."
(etypecase destination
(pathname
(let ((stream (apply #'open destination :direction :output args)))
(unwind-protect
(funcall function stream)
(close stream))))
(string
(assert (array-has-fill-pointer-p destination)
nil "Destination string doesn't have a fill-pointer")
(let ((result nil))
(with-output-to-string (stream destination)
(setf result (funcall function stream)))
result))
(stream
(funcall function destination))
(null
(with-output-to-string (stream)
(funcall function stream)))
((eql t)
(funcall function *standard-output*))))
(defmacro with-output-to-destination ((var destination &rest args) &body body)
"Evaluate BODY with VAR bound to a stream created from DESTINATION.
If DESTINATION is a pathname, then open the file for writing. ARGS are used in the OPEN call.
If it is a string with a fill-pointer, use WITH-OUTPUT-TO-STRING to create a stream for it.
If it is a stream, then it is used as it is.
If it is NIL, then WITH-OUTPUT-TO-STRING is used to create the stream.
If it is T, then *STANDARD-OUTPUT* is used for the stream."
`(call-with-output-to-destination ,destination (lambda (,var) ,@body) ,@args))
Strings are not interpreted as pathnames anymore, to avoid any confusion. And are required to have a fill-pointer.
That sounds good to me, if you'd like to make an MR.
Hi.
I use this macro in some of my systems, to write to an output stream created from a "destination" spec:
*standard-output*
.Similar to CL:FORMAT destination argument, but also writes to files.
I've opened this issue in case you are interested and would like to include in Serapeum.