Shammer's Philosophy

My private adversaria

with-open-passive-socket を自作

with-open-file や、with-open-socket のように、

  • ストリームへのアクセスシンボルを呼出側で定義できるようにする
  • passive socket(server socket) とわかっているので、そのためのオプションを指定せずに済むようにする
  • クローズをするようにしたいが、わかりきった処理なので隠蔽しておきたい

の要件を満たすようなマクロを自作。ストリームへのアクセスシンボルを呼出側で定義する、というのが予想以上に難しかった。
gensym とかマクロ定番の処理を使うといけないんだ、、、おそらく。まあ、gensym 使ってしまったら、呼出側で指定したシンボルは意味をなさないから当然だろうか。
できあがった実装はこんな感じ。

(defmacro with-open-passive-socket (&rest body)
  (let ((server-symbol-value (caar body))
	(server-listen-port (cadar body)))
    `(let ((,server-symbol-value (open-socket-server ,server-listen-port)))
       (unwind-protect
	    (when ,server-symbol-value
	      ,(cadr body))
	 (unless (null ,server-symbol-value)
	   (format t "Close server socket...~%")
	   (close-server-socket ,server-symbol-value))))))

使うときは以下のようにする。

(with-open-passive-socket (server port)
  ...

server というのが、本体の中で使用できるサーバーストリームへのアクセス。
port は整数のシンボルで、これが listen port になる。
open-socket-server や close-server-socket 等は、処理系に応じた関数の wrapper 的なもの。clozure であれば、

(defun open-socket-server (port)
  (make-socket :connect :passive
	       :reuse-address t
	       :local-port port))

というような感じ。処理系ごとにこういう関数を用意すれば汎用的に使用できるはず。