関数を生成するマクロ・その5
関数を生成するマクロ・その4 - Shammerismでの課題を解決できた。バッククォートより後の内容が展開式になるが、展開式の中で展開式で宣言される defun の関数名を作成していたのが原因だった。つまりはケアレスミスのようなものだ。うまくいかなかった例は以下のように試していた。
? (defmacro generate-semaphore-getter (name) `(let ((semaphore (make-semaphore)) (getter (string-upcase (concatenate 'string "get-semaphore-for-" ,name)))) (defun ,(intern getter) () semaphore))) ;Compiler warnings : ; In GENERATE-SEMAPHORE-GETTER: Undeclared free variable GETTER GENERATE-SEMAPHORE-GETTER ?
ここで、(defun ,(intern getter) () semaphore) の ,(intern getter)を、(intern getter)としたり、',(intern getter) や `,(intern getter) としてみたりと、いろいろとやったがどこかしらでエラーになる。問題は defun の中身じゃなくて、getter をバッククォートの中で宣言していたことだった。間違いに気付いて変更したバージョンが以下。
? (defmacro generate-semaphore-getter (name) (let ((getter (string-upcase (concatenate 'string "get-semaphore-for-" name)))) `(let ((semaphore (make-semaphore))) (defun ,(intern getter) () semaphore)))) GENERATE-SEMAPHORE-GETTER ? (pprint (macroexpand-1 '(generate-semaphore-getter "XXX"))) (LET ((SEMAPHORE (MAKE-SEMAPHORE))) (DEFUN GET-SEMAPHORE-FOR-XXX () SEMAPHORE)) ? (generate-semaphore-getter "xxx") GET-SEMAPHORE-FOR-XXX ? (get-semaphore-for-xxx) #<SEMAPHORE #x302000DE321D> ? (eq (get-semaphore-for-xxx) (get-semaphore-for-xxx)) T ?
これで実装もスッキリした。この例では、検証のために関数を生成するマクロ・その4 - Shammerismでは入れていた public-symbol とかを省略しているけれども、、、念の為書いておこう。
? (defmacro generate-semaphore-getter (name) (let ((getter (string-upcase (concatenate 'string "get-semaphore-for-" name)))) `(let ((semaphore (make-semaphore))) (defun ,(intern getter) () semaphore) (public-symbol ,(intern getter)) (export-public-symbol)))) GENERATE-SEMAPHORE-GETTER ? (pprint (macroexpand-1 '(generate-semaphore-getter "YYY"))) (LET ((SEMAPHORE (MAKE-SEMAPHORE))) (DEFUN GET-SEMAPHORE-FOR-YYY () SEMAPHORE) (PUBLIC-SYMBOL GET-SEMAPHORE-FOR-YYY) (EXPORT-PUBLIC-SYMBOL)) ? (generate-semaphore-getter "YYY") NIL ? (get-semaphore-for-yyy) #<SEMAPHORE #x302000E076CD> ? (eq (get-semaphore-for-yyy) (get-semaphore-for-yyy)) T ?