Posted 2021-12-18 13:32:34 GMT
Lisp一人 Advent Calendar 2021 19日目の記事です。
Common Lispでは処理系依存の処理を記述するのに#-
/#+
を使いますが、込み入った条件の場合は結構面倒で、それに伴なったデフォルトケースの記述も面倒です。
既に色々解決方法は考えられていると思いますが、自分もちょっと考えてみました。
(defmacro feature-case (&body clauses)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(macrolet ((run ()
(read-from-string
,(with-output-to-string (out)
(format out "~&(cond ~%")
(dolist (c clauses)
(typecase (cadr c)
(STRING
(format out
"~&#+~A (T (progn ~A))~%"
(car c)
(cadr c)))
(T (if (eql 'otherwise (car c))
(format out
"~&(T ~S)~%"
`(progn ,@(cdr c)))
(format out
"~&#+~A (T ~S)~%"
(car c)
`(progn ,@(cdr c)))))))
(format out "~&)~%")))))
(run))))
入れ子にもできます。
(feature-case
(lispworks
(feature-case
(lispworks7+
(feature-case
(lispworks8 'foo)))))
(otherwise nil))
→ foo
なお、読取時に存在するパッケージが処理系によってまちまちなため、マクロ展開時に存在しないパッケージを読み込んでエラーにならないようにする必要があります。
ここはポータブルには回避しようがないので、文字列で記述してもらうことにしました。
(feature-case
((and lispworks6 (or win32 linux))
"(defmethod environment-p ((environment lexenv::environment))
t)")
((and lispworks7+ (or win32 linux))
"(defmethod environment-p ((environment compiler::environment))
t)")
(otherwise nil))
(feature-case
(lispworks 'foo 'bar 'baz)
(allegro "foo:foo foo:bar foo:baz")
(otherwise 'other))
多分、複雑な場合はファイルを処理系の条件ごとに分割してしまった方が良いでしょう。
実際、基盤になるライブラリでは結構ファイルを分割しているのを目にします。
■
HTML generated by 3bmd in LispWorks 8.0.0