微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

何时消除警告的好例子?

如何解决何时消除警告的好例子?

这个问题与之前关于 programmatically generating symbol macros一个问题有些相关。我在一个方便的宏中使用该函数,该宏会抛出未定义的变量警告。这个宏和函数

(defmacro define-data (d body &optional doc)
  (if (and doc (not (stringp doc))) (error "Documentation is not a string"))
  `(let* ((d-str (string ',d))
          (old-package *package*)
          (*package* (if (find-package d-str)    ;exists?
                         (find-package d-str)    ;yes,return it
                         (make-package d-str)))) ;no,make it
     ;; Should we have an eval-when (:compile-toplevel) here?
     (defparameter,d,body,doc)
     (export ',d old-package)
     (define-column-names,d)))

(defun define-column-names (d)
  (maphash #'(lambda (key index)
           (eval `(cl:define-symbol-macro,key (cl:aref (columns,d),index))))
       (ordered-keys-table (slot-value d 'ordered-keys))))

旨在类似于 defparameter,但另外通过定义为用户设置了一些细节:

  1. 一个名为 d 的包
  2. 当前包中的参数,其中包含将被 body 吸入的数据
  3. d 中的符号宏,用于访问各个数据向量

如果我使用 REPL 中的 defparameter,然后调用 define-column-names,一切都很好。但是,当使用宏时,我得到:

; in: DEFINE-COLUMN-NAMES FOO
;     (DEFINE-COLUMN-NAMES CL-USER::FOO)
; 
; caught WARNING:
;   undefined variable: CL-USER::FOO

我怀疑这是因为编译器无法知道在调用 define-symbol-macro 时实际上会定义 FOO。一切正常,但我不希望警告吓到用户,所以我想压制它。不过我讨厌压制警告,所以我想我会来这里征求第二意见。

编辑:我已将答案标记为正确,因为它确实正确回答了所问的问题。有关问题的答案,请参阅我的评论

解决方法

我对标题中“何时消除警告”问题的回答是:如果是你自己的代码,那么在任何情况下都不要。如果是别人的代码,那就重写它不要警告,除非你不能。

至于解决这个问题我还没有认真考虑过,但问题是你肯定希望 defparameter 处于顶层以便编译器可以看到它,而它不能如果它在 let 内,确实如此。但是您可以轻松地将其提升到顶级,因为它不依赖于 let 内的任何内容。

我非常确定您希望宏的其余部分在编译时发生,因为您肯定希望符号宏在编译时可用。所以对第一个宏的尝试是(注意我已经修复了文档字符串的处理:(defparameter foo 1 nil) 不好):

(defmacro define-data (d body &optional doc)
  (when (and doc (not (stringp doc)))
    (error "Documentation is not a string"))
  `(progn
     (defparameter,d,body,@(if doc (list doc) '()))
     (eval-when (:compile-toplevel :load-toplevel :execute)
       (let* ((d-str (string ',d))
              (old-package *package*)
              (*package* (if (find-package d-str)    ;exists?
                             (find-package d-str)    ;yes,return it
                           (make-package d-str)))) ;no,make it
         (export ',d old-package)
         (define-column-names,d)))))

附带说明:虽然我认为以编程方式定义符号宏很困难,因为 CL 出于某种原因将其排除在外,但我认为我个人会使用其他方法而不是这种方法,因为 eval 是太可怕了。然而,这只是我:如果你想这样做,你确实需要 eval 我认为(这是非常罕见的!)。

,

我不确定 define-columns-names 究竟是如何工作的,所以我用一个返回 d 的存根函数替换了它。

另请注意,您可以使用 check-type 并且应该尽量不要在生成的代码中注入符号,这会引入可以使用 gensym 避免的潜在变量捕获。

据我所知,您不能按照您的评论建议使用 eval-when(有关详细信息,请参阅 Issue EVAL-WHEN-NON-TOP-LEVEL Writeup)。

但是如果我在调用中声明符号是特殊的,我不会发出警告。

(defmacro define-data (d body &optional doc)
  (check-type doc (or null string))
  (check-type d symbol)
  (let ((d-str (string d)))
    (alexandria:with-gensyms (old-package)
      `(let* ((,old-package *package*)
              (*package* (if (find-package,d-str)    ;exists?
                             (find-package,d-str)    ;yes,return it
                             (make-package,d-str)))) ;no,make it
         (defparameter,doc)
         (export ',old-package)
         (locally (declare (special,d))
           (define-column-names,d))))))

将调用扩展为对 define-column-names 的调用也有点奇怪,它反过来评估在运行时构建的表单。我认为在宏观扩展期间可以做所有你想做的事情,但正如前面所说,你想要做什么对我来说有点不清楚。我的想法是将 define-column-names 替换为:

,@(expand-column-names-macros d)

... 其中 expand-column-names-macros 构建 define-symbol-macro 表单列表。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。