【问题标题】:Suppress style-warning on make-instance in sbcl抑制 sbcl 中 make-instance 的样式警告
【发布时间】:2018-02-04 08:24:37
【问题描述】:

我有两个包,每个包都定义了一个类。第二个类继承自第一个类,但有一个同名的插槽。其意图确实是覆盖插槽。

(defpackage :foo
  (:use :cl)
  (:export foo))

(in-package :foo)

(defclass foo () ((s)))

(defpackage :bar
  (:use :cl :foo)
  (:export bar))

(in-package :bar)

(defclass bar (foo) ((s)))

当我创建bar 的实例时,sbcl 会给出一个有用的警告

(make-instance 'bar)

STYLE-WARNING:
   slot names with the same SYMBOL-NAME but different SYMBOL-PACKAGE (possible
   package problem) for class #<STANDARD-CLASS BAR:BAR>:
       (FOO::S BAR::S)

由于这是预期的行为,我可以像这样抑制该警告:

(handler-bind (#+SBCL (style-warning #'muffle-warning))
  (make-instance 'bar))

但是,我希望 bar 类的用户能够在不收到警告的情况下创建实例。

我可以编写一个包含前一个代码块中代码的包装函数,但是否可以在调用 (make-instance 'bar) 之前抑制警告而不消除所有样式警告?

【问题讨论】:

  • 如果您打算覆盖该插槽,则必须从其他包导入名称(或使用包限定名称)。照原样,您有两个插槽,FOO::SBAR::S
  • @jkiiski Doh!很好,谢谢。
  • 这正是警告的内容;-)
  • 请给出答案。

标签: common-lisp warnings sbcl clos suppress


【解决方案1】:

Common Lisp 中的符号通常属于一个包。当 Lisp 阅读器遇到一个新符号时,它将把它放在当前包中(由IN-PACKAGE 设置),除非从其他地方导入了同名的符号。通过在包和符号名称之间放置一个冒号(或内部符号的两个冒号),可以用包名称编写符号。

如果我们将包前缀添加到您的代码中,正如 Lisp 阅读器所看到的那样,错误会变得很容易看到:

(cl:defpackage keyword:foo
  (keyword:use keyword:cl)
  (keyword:export cl-user::foo)) ;Makes FOO:FOO an external symbol, which will be 
                                 ; imported with the (:USE ... :FOO) below.
(cl:in-package keyword:foo)

(cl:defclass foo:foo () ((foo::s))) ;FOO::S is not exported, so it 
                                    ; is an internal symbol. It will 
                                    ; not be imported by the :USE-clause.

(cl:defpackage keyword:bar
  (keyword:use keyword:cl keyword:foo) ;Import all external symbols from CL and FOO.
  (keyword:export cl-user::bar))

(cl:in-package keyword:bar)

(cl:defclass bar:bar (foo:foo) ;FOO:FOO was imported from the other package.
  ((bar::s))) ;FOO::S (an internal symbol) wasn't imported, so a new
              ; symbol was interned in BAR.

BAR:BAR 类实际上有两个插槽,FOO::SBAR::S。插槽名称具有相同的SYMBOL-NAME,但具有不同的SYMBOL-PACKAGE。这很少是程序员想要的,因此 SBCL 会给出警告。为了解决这个问题,你应该导出FOO::S,这样在包BAR中写不合格的S将引用相同的符号。或者,您可以将插槽名称写入包限定形式,但通常不建议使用其他包的内部符号。

【讨论】:

    【解决方案2】:

    这个警告似乎在这里很有用,但是如果你发现自己处于必须消除它的情况,那么合适的地方就是类终结:

    #+sbcl
    (defmethod sb-mop:finalize-inheritance :around ((c (eql (find-class 'bar))))
       (handler-bind ((sb-int:simple-style-warning #'muffle-warning))
         (call-next-method)))
    

    您在创建新实例时出错,但是当我测试代码时,在 REPL 中定义类时发出了错误信号。这是在类终结期间发出错误信号的线索,这必须在 defclass 之后(但不一定紧随其后)并且在分配第一个实例之前完成。见Class finalization protocol

    调用 finalize-inheritance 的确切时间取决于类元对象的类;对于标准类,它在类的所有超类都被定义后的某个时间被调用,但不迟于类的第一个实例被分配时(通过 allocate-instance)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 2011-03-31
      • 2019-10-22
      • 2014-12-19
      相关资源
      最近更新 更多