[Openmcl-devel] How to access Cocoa objects returned via indirect references (i.e. NSError)

Gary Byers gb at clozure.com
Mon Jul 24 23:14:41 PDT 2006



On Tue, 25 Jul 2006, Phil wrote:

>
> On Jul 24, 2006, at 11:35 PM, Gary Byers wrote:
>
>>
>> I'm not sure that I understand the question,
>
> You caught the essence of my question despite my poor wording.
>
>>
>> Note that if something's returned by reference, you'd generally
>> do something like:
>>
>>  (let* ((ns-error-ptr :id))

Of course, if I'd been paying more attention, I'd have used RLET instead
of LET*.

>>    (send some-message ... ns-error-ptr ...)
>>    (let* ((the-ns-error (pref ns-error-ptr :id)))
>>      ;; At this point, THE-NS-ERROR should be an NSError (or possibly
>>      ;; a NULL pointer if there was no error ...
>>      ...)))
>>
>> (That's no different from how other things are returned by reference.)
>>
>
> That is what I was struggling with and mostly solved my problem.  The
> first let with (ns-error-ptr :id) resulted in an error (The value :ID
> is not of the expected type MACPTR) in the send some-message.  Here's
> the relevant snippet:
>
> (ccl::with-autorelease-pool
>   (let* ((error-ptr (ccl::make-objc-instance 'ns-error)) ; :id
> results in error
>             (the-xml (ccl::make-objc-instance 'ns-xml-document
>
>  :init-with-contents-of-url (ccl::send (ccl::@class ns-url)
>
>                                                                :url-
> with-string #@"http://blah")
>
>  :options (logior #$NSXMLNodePreserveWhitespace
>
>                              #$NSXMLNodePreserveCDATA)
>
>  :error error-ptr)))
>     (if error-ptr
>       (let ((the-error (ccl::pref error-ptr :id)))
>         (format t "An error was encountered: ~a~%" the-error)))
>     the-xml))
>

(ccl::with-autorelease-pool
   ;; First, we (stack) allocate a pointer to a pointer to an
   ;;  NSObject (something of foreign type :id).
   (rlet ((error-ptr :id))
     ;; At this point, the value of ERRROR-PTR is well-defined (it's
     ;; an address on a stack, and if we run this in the same thread
     ;; and in the same execution context, it'll always be pointing
     ;; to the same address.)
     ;; Since we haven't done anything to initialize the word of
     ;; memory that ERROR-PTR points to, the result of doing
     ;; (pref error-ptr :id) at this point is a little less deterministic;
     ;; the value is "whatever (garbage) was in that stack location".
     ;; We don't care; all that we've done is to reserve a word of
     ;; memory.  We'll pass the address of that word to an ObjC method,
     ;; and it may store the address of some (constant or other) NSError
     ;; object there.
     ;;
     ;; On second thought, let's be defensive and store a NULL pointer
     ;; there.
     ;;
     (setf (pref error-ptr :id) (%null-ptr))


     (let* ((the-url-object
               (send (@class "NSURL")
                     :url-with-string #@"http:/blah"))
 	   (the-xml-document
 	       (make-objc-instance 'ns:ns-xml-document
 		  :with-contents-of-url the-url-object
                   :options (logior #$NSXMLNodePreserveWhitespace
                                 #$NSXMLNodePreserveCDATA)
 		  :error error-ptr)))
     ;; Were we able to create an NSXMLDocument ?
     (if (%null-ptr-p the-xml-document)
       (error "Error creating NSXMLDocument, NSError = ~s"
              (pref error-ptr :id))
       the-xml-document))))

The above is untested code; I'm working on something else at the moment
and can't easily test it (the only machine that I have free is running
10.3.9); I think that that's the general idea, however, and barring
typos or other brain damage it should work fine.

It's hard to know just by looking at a C/ObjC function prototype
whether something passed by reference is an input, output, or both.
In the case of this message, the parameter associated with the
:error component of the messagee is something returned by the message,
so the caller needs to reserve space into which the callee can store
that extra return value.

Actually, I was able to get that to work (so just beware of the fact
that the code above neglects to use ccl:: prefixes where it should ...),
and running it produced:

> Error: Error creating NSXMLDocument, NSError = #<NS-ERROR NSError "zero byte resource" Domain=NSURLErrorDomain Code=-1014 UserInfo={
>            NSErrorFailingURLKey = http:/blah; 
>            NSErrorFailingURLStringKey = "http:/blah"; 
>            NSLocalizedDescription = "zero byte resource"; 
>        } (#x38A900)>

which (I assume) is a complaint about the bogus NSURL.











More information about the Openmcl-devel mailing list