[Openmcl-devel] Trouble defining ObjC classes with struct slots

Gary Byers gb at clozure.com
Wed May 23 15:45:54 PDT 2007

On Wed, 23 May 2007, Daniel Dickison wrote:

> I'm trying to create an Objective-C class in OpenMCL via the bridge
> which has a couple of NSPoint slots, and I'm running into a couple of
> problems.  I'm not sure if the following behaviors are intended, but
> they sure stumped me for a while!
> 1. If I define the slot to have a :foreign-type of :<NSP>oint, then
> when I access the slot I get a generic foreign pointer which is not
> recognized as an :<NSP>oint.  Example:
> 	(defclass a-point (ns:ns-object)
> 	  ((point :foreign-type :<NSP>oint))
> 	  (:metaclass ns:+ns-object))
> 	(setf p (make-instance 'a-point))
> 	(ns:ns-point-p (slot-value p 'point))
> 	==> nil
> This means I can't use functions like ns:ns-point-x with the slot,
> which in inconvenient but work-aroundable using (pref (slot-value p
> 'point) :<NSP>oint.x).  Is there a way to convince the runtime that
> these foreign slots are in fact NSPoints?

There should be something like

(ASSERT-FOREIGN-POINTER_TYPE <p> <foreign-type>),
(which does what RLET and MAKE-RECORD do), and SLOT-VALUE (which pretty
much has to cons up a MACPTR in cases like this).

;;; This would be useful outside of ObjC and should probably be
;;; exported from CCL.

(in-package "CCL") ;

(defmacro assert-foreign-pointer-type (pointer foreign-type)
   (let* ((p (gensym)))
   `(let* ((,p ,pointer))
        (load-time-value (%foreign-type-or-record ',foreign-type))))

;;; We'd like SLOT-VALUE to assert that the pointer it returns points
;;; to an NSRect; until it does so:

(assert-foreign-pointer-type (slot-value (make-instance 'a-point) 'point)

#<NS-POINT 5.180654D-318,0 (#x122B56A0) #x300041AA69DD>

> 2. If I define the slot to be a CLOS slot (i.e. no :foreign-type
> option), then I can store NSPoints in the slot and get back NSPoints
> as expected, BUT, any initialization I do during the ObjC -init
> method gets overridden by the :initform.  It seems that CLOS slots
> get re-initialized after the ObjC -init method runs, so the ObjC
> initializations get wiped out.  Example:
> 	(defclass b-point (ns:ns-object)
> 	  ((point :initform (ns:make-ns-point 1.0 2.0)))
> 	  (:Metaclass ns:+ns-object))
> 	(objc:defmethod (#/init :id) ((self b-point))
> 	  (format t "Before: ~A   " (slot-value self 'point))
> 	  (setf (slot-value self 'point) (ns:make-ns-point 3.0 4.0))
> 	  (format t "After: ~A" (slot-value self 'point))
> 	  self)
> 	(setf p (make-instance 'b-point))   ;; Before: #<NS-POINT 1,2...>
> After: #<NS-POINT 3,4...>
> 	(slot-value p 'point)
> 	==> #<NS-POINT 1,2...>
> This as also work-aroundable by doing the CLOS slot initialization in
> an initialize-instance :after method, but it seems wrong to do the
> same initialization in 2 different places depending on whether Lisp
> or Objective-C created the instance.

I think that that's a bug, but there are some chicken-and-egg issues
here. (In order to be able to refer to the object's lisp slots from an
ObjC init method, it's necessary to run INITIALIZE-INSTANCE - with no
initargs - so that the slots' initforms are executed.  The ObjC init
method is often called (as part of ALLOCATE-INSTANCE) by
MAKE-INSTANCE, which then turns around and does INITIALIZE-INSTANCE
with initargs.  That shouldn't cause any slots to be "initialized
twice", but it's certainly unintuitive that INITIZLIZE-INSTANCE
should run twice per instance (and it probably offers ways to lose.))

The bug is that ALLOCATE-INSTANCE creates a "slots vector" for
the instance even if one was created by virtue of the fact that
a lisp slot was referenced in an ObjC #/init method.)

> Daniel
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list