From: Larry Hunter

Subject: Re: Sharing streams with foreign (C) functions

Date: 1998-2-12 18:12


Liam Healy asks:

  Is it possible to share/send a stream with a foreign function?  I would
  like to have with-open-file open a file, then give the stream (or some
  mapping of that stream) to a C function that expects a (C) stream (e.g.,
  something that would result from the C function fopen).  This C function
  would put output to that stream and then return to the calling CL
  function.

How about using the foreign function interface to make lisp equivalents of
fopen and friends?  I do this sometimes to speed up IO, e.g.:

(load "" :unreferenced-lib-names (list (ff:convert-to-lang "fopen" :language :c)))
(load "" :unreferenced-lib-names (list (ff:convert-to-lang "fclose" :language :c)))
(load "" :unreferenced-lib-names (list (ff:convert-to-lang "getc" :language :c)))
(load "" :unreferenced-lib-names (list (ff:convert-to-lang "putc" :language :c)))

(FF:defforeign 'fopen :arguments '(simple-string simple-string) :return-type :fixnum)
(FF:defforeign 'fclose :arguments '(fixnum) :return-type :fixnum)
(FF:defforeign 'getc :arguments '(fixnum) :return-type :fixnum
	       :call-direct t :arg-checking nil :callback nil)
(FF:defforeign 'putc :arguments '(fixnum fixnum) :return-type :fixnum
	       :call-direct t :arg-checking nil :callback nil)

;; Define a pseudo-stream that keeps C-style port number, the associated filename and a boolean for
;; whether it is open or not.

(defstruct fast-io-stream filename port open?)

;; To open a fast-io-stream, call fopen and put the port in the appropriate struct.  Valid directions
;; are :input :output :io (may not work without fseeks, etc.) and :append.  All append streams are
;; read-write.  There are lots of synonyms for these directions

(defun fast-io-open (filename &key (direction :input))
  (setq direction
	(cond 
	  ((member direction '("r" "read" "input" "in" read input in :input)
		   :test #'equal) "r")
	  ((member direction '("w" "write" "output" "out" write output out :output)
		   :test #'equal) "w")
	  ((member direction '("r+" "rw" "io" io :io) :test #'equal) "r+")
	  ((member direction '("a" "a+" "append" append :append) :test #'equal) "a+")))
  (make-fast-io-stream :filename filename :port (fopen filename direction) :open? t))

(defun fast-io-close (str)
  (fclose (fast-io-stream-port str))
  (setf (fast-io-stream-open? str) nil)
  str)

(defun fast-io-read-char (str)
  (code-char (getc (fast-io-stream-port str))))

(defun fast-io-write-char (char str)
  (putc (char-code char) (fast-io-stream-port str)))


;; From here I imagine that you can build up a LISP interface to the stream
;; (e.g. with-open-fast-io-stream) as well as pass the port to your C code.  

Larry