在生成实例期间如何强制检查插槽的类型? [英] How to force slot's type to be checked during make-instance?

查看:94
本文介绍了在生成实例期间如何强制检查插槽的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下类声明:

 (defclass foo-class()
((bar :initarg:bar
:type list)))

当我创建此实例时类, make-instance 不会检查传递的参数是否满足插槽类型。因此,我可以这样创建无效对象:

 > (make-instance‘foo-class:bar’some-symb)
#< FOO-CLASS {102BEC5E83}>

但是,我希望看到的行为类似于创建a实例的行为struct,检查类型的地方:

 (defstruct foo-struct 
(bar nil:type list))

> (make-foo-struct:bar’some-symb)
;;引发竞争:
;;
;;
;;某些符号
;;不是类型
;;列出
;;设置结构FOO-STRUCT

的插槽BAR时,有什么方法可以实现?

解决方案

对于结构和CLOS实例,是否都不确定是否检查了插槽类型



许多实现将对结构执行此操作-但不是全部。



很少有实现对CLOS实例执行此操作-Clozure CL实际上会执行例如。



SBCL还可以检查CLOS插槽类型-当安全性很高时:

  *(声明(优化安全性))

NIL
*(progn
(defclass foo-class()
((bar:initarg:bar
:type list)))
(make-instance'foo-class:bar'some-symb))

在类型错误上调用调试器:SOME-SYMB的值为不是LIST类型。

键入HELP以获取调试器帮助,或(SB-EXT:EXIT)退出SBCL。

重新启动(按数字或可能缩写的名称调用):
0:[ABORT]退出调试器,返回顶层。

((SB-PCL :: SLOT-TYPECHECK LIST)SOME-SYMB)
0]

否则如何做?



这是一种高级科目,可能需要一些CLOS元数据-对象协议黑客。两种变体:




  • 为SHARED-INITALIZE定义一个检查init参数的方法。


  • 为您的类定义一个元类,并在SET-SLOT-VALUE-USING-CLASS上定义一个方法。但是随后您需要确保您的实现确实提供并使用SET-SLOT-VALUE-USING-CLASS。这是通用功能,是MOP的一部分。有些实现提供了它,但是有些实现仅在需要时使用它(否则设置插槽可能会降低速度)。




对于后者,这是自建的 SBCL 版本,用于检查写入插槽的类型:



首先是元类:

 ;首先是检查插槽写入的类的元类
(defclasschecked-class(s​​tandard-class)
())

;这是一种MOP方法,可能对便携式版本
使用CLOSER-MOP(defmethod sb-mop:validate-superclass
((classchecked-class)
(superclass standard-class))
t)

现在我们检查该元类的所有插槽写入:

 ;这是一种MOP方法,可能对便携式版本
(defmethod(setf sb-mop:slot-value-using-class)使用CLOSER-MOP:在
(new-value(class checked-class) )对象槽位)
(断言(typep新值(sb-mop:槽口定义类型槽位))
()
新值〜a不是〜a类型object〜a slot〜a
新值(sb-mop:slot-definition-type slot)object slot))

我们的示例类使用该元类:

 (defclass foo-class()
(((bar:initarg:bar:type list))
(:元类已检查类))

使用它:

  *(make-instance'foo-class:bar 42)

调试程序在线程
中的SIMPLE-ERROR上调用#<线程正在运行的主线程{10005605B3}> ;:
新值42在对象#<中的类型不是LIST
; FOO-CLASS {1004883143}>
slot#< STANDARD-EFFECTIVE-SLOT-DEFINITION COMMON-LISP-USER :: BAR>

键入HELP以获取调试器帮助,或(SB-EXT:EXIT)退出SBCL。

重新启动(按数字或可能缩写的名称调用):
0:[继续]重试断言。
1:[ABORT]退出调试器,返回顶层。


Let's say I have the following class declaration:

(defclass foo-class ()
  ((bar :initarg :bar
        :type list)))

When I create an instance of this class, make-instance won't check whether passed arguments satisfy types of slots. So, I can create "invalid" objects this way:

> (make-instance 'foo-class :bar 'some-symb)
#<FOO-CLASS {102BEC5E83}>

However, what I'd like to see is the behavior similar to the creation of an instance of a struct, where the types are checked:

(defstruct foo-struct
  (bar nil :type list))

> (make-foo-struct :bar 'some-symb)
;; raises contition:
;;
;; The value
;; SOME-SYMB
;; is not of type
;; LIST
;; when setting slot BAR of structure FOO-STRUCT

Is there any way to achieve this?

解决方案

Whether slot types are being checked or not is undefined for both structures and CLOS instances.

Many implementations will do it for structures - but not all.

Few implementations will do it for CLOS instances - Clozure CL actually does it for example.

SBCL also can check CLOS slot types - when safety is high:

* (declaim (optimize safety))

NIL
* (progn
(defclass foo-class ()
  ((bar :initarg :bar
        :type list)))
(make-instance 'foo-class :bar 'some-symb))

debugger invoked on a TYPE-ERROR: The value SOME-SYMB is not of type LIST.

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

((SB-PCL::SLOT-TYPECHECK LIST) SOME-SYMB)
0] 

How to do it otherwise?

This is kind of an advanced subject which probably needs some CLOS meta-object-protocol hackery. Two variants:

  • define a method for SHARED-INITALIZE which checks the init arguments.

  • define a metaclass for your class and a method on SET-SLOT-VALUE-USING-CLASS . But then you need to be sure that your implementation actually provides AND uses SET-SLOT-VALUE-USING-CLASS. This is a generic function, which is part of the MOP. Some implementations provide it, but some are only using it when requested (otherwise setting a slot may get a speed penalty).

For the latter here is self-built SBCL version to check types for writing slots:

First the metaclass:

; first a metaclass for classes which checks slot writes
(defclass checked-class (standard-class)
  ())

; this is a MOP method, probably use CLOSER-MOP for a portable version
(defmethod sb-mop:validate-superclass
           ((class checked-class)
            (superclass standard-class))
   t)

Now we check all slot writes for that metaclass:

; this is a MOP method, probably use CLOSER-MOP for a portable version    
(defmethod (setf sb-mop:slot-value-using-class) :before
              (new-value (class checked-class) object slot)
  (assert (typep new-value (sb-mop:slot-definition-type slot))
      ()
    "new value ~a is not of type ~a in object ~a slot ~a"
    new-value (sb-mop:slot-definition-type slot) object slot))

Our example class uses that metaclass:

(defclass foo-class ()
  ((bar :initarg :bar :type list))
  (:metaclass checked-class))

Using it:

* (make-instance 'foo-class :bar 42)

debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {10005605B3}>:
  new value 42 is not of type LIST
  in object #<FOO-CLASS {1004883143}>
  slot #<STANDARD-EFFECTIVE-SLOT-DEFINITION COMMON-LISP-USER::BAR>

Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Retry assertion.
  1: [ABORT   ] Exit debugger, returning to top level.

这篇关于在生成实例期间如何强制检查插槽的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆