什么时候消除警告的好例子?

Good example of when to muffle warnings?

这个问题与之前 上的一个问题有些相关。我在抛出未定义变量警告的便捷宏中使用该函数。这个宏和函数:

(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. 的包
  3. 当前包中的一个参数,其中包含将被body
  4. 吸入的数据
  5. 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 实际上会被定义。一切正常,但我不想让警告吓到用户,所以我想抑制它。不过我讨厌压制警告,所以我想来这里征求第二意见。

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

我不确定 define-columns-names 是如何工作的,所以我用 returns 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 ,d ,body ,doc)
         (export ',d ,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 表单列表。

我对标题中 'when to muffle warnings' 问题的回答是:如果这是你自己的代码,那么在任何情况下都不要 。如果是别人的代码,那就重写不警告,除非你不行。

至于解决这个问题,我还没有认真考虑过这个问题,但问题是你肯定希望 defparameter 位于 top-level 以便编译器可以看到它,并且如果它在 let 内,则不可能。但是你可以将它提升到顶层,因为它不依赖于 let.

中的任何内容

然后我非常确定您希望宏的其余部分在编译时发生,因为您肯定希望 symbol-macros 在 compile-time 可用。所以第一个宏的尝试是(注意我已经修复了文档字符串的处理:(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 出于某种原因将其排除在外,但我认为我个人会使用其他方法而不是这种方法,因为 [=15= 】 太可怕了。然而,这只是我:如果你想这样做,你确实需要 eval 我认为(这是真的很少见!)。