I'm trying to make a single-file, cross-implementation program that involves some networking. I only need a handful of relatively high-level functions: connect to a port on a host, write octets, read available octets, and disconnect.
Most similar attempts I've seen involve functions with a bunch of
intermixed #+foo and #-bar, and I find that kind of ugly. It's a
little less ugly to have something approximately like
"foo-defs.lisp"), but that defeats my single-file goal.
I decided to give it a try with CLOS, using an object to represent the current implementation and defining methods that specialize on the implementation class. For example, here's part of the file to support LispWorks:
;;; LispWorks (define-implementation-package :lispworks #:qn.lw (:prep (require "comm")) (:import-from #:comm #:open-tcp-stream) (:import-from #:system #:wait-for-input-streams) (:export #:open-tcp-stream #:wait-for-input-streams #:lisp)) (defclass qn.lw:lisp (lisp connections-are-streams) ()) (defmethod %open-connection (host port (lisp qn.lw:lisp)) (qn.lw:open-tcp-stream host port :direction :io :read-timeout 0 :element-type 'octet)) (defmethod %read-octets :before (buffer connection (lisp qn.lw:lisp)) (declare (ignore buffer)) (qn.lw:wait-for-input-streams (list connection))) #+lispworks (setf *lisp* (make-instance 'qn.lw:lisp))
I split a subset of the program into its own file with just the networking stuff: quicknet.lisp. It supports SBCL, CLISP, LispWorks, Allegro Common Lisp, ECL, and Clozure CL.
I'd love to get some feedback. What do you think of the approach? Is there something I should do differently or better?