[Openmcl-devel] Sending a message to a Cocoa object using parameters
Phil
pbpublist at gmail.com
Tue Sep 9 20:52:42 PDT 2008
Gary Byers wrote:
> ? (in-package "CCL") ; my init file put me in the CCL package. Sorry.
>
> ? (macroexpand-1 '(send (the ns:ns-window w) 'title))
> (WITH-NS-EXCEPTIONS-AS-ERRORS (RLET NIL (OBJC-MESSAGE-SEND (THE NS:NS-WINDOW W) "title" :ID)))
>
> CCL::WITH-NS-EXCEPTIONS-AS-ERRORS is a bloodcurdling hack that sets up
> an ObjC exception handler around a body of code and signals a lisp error
> if an NSException is raised. (It's generally necessary to do this and
> the inverse in order to prevent either the lisp or ObjC runtimes from
> throwing exceptions past each other; the idea is that - sooner or later -
> something on either the Lisp or ObjC side will catch something that it
> knows how to handle. The main event loop has a handler which basically
> logs any exceptions that occur during event processing and tries to
> process the next event.)
>
> SEND wraps an RLET around the message send to deal with messages that
> return structures and/or pass structures by value (I forget which,
> and am too lazy to check at the moment.) The actual message sending
> happens inside CCL::OBJC-MESSAGE-SEND, which is itself a macro:
>
> ? (macroexpand-1 '(OBJC-MESSAGE-SEND (THE NS:NS-WINDOW W) "title" :ID))
> (EXTERNAL-CALL "_objc_msgSend" :ID (THE NS:NS-WINDOW W) :<SEL> (@SELECTOR "title") :ID)
>
I got this far and realized things were getting ugly fast (an impressive
feat of coding, but past my comfort level that this was the right approach)
> It basically expands into a foreign function call to an ObjC runtime routine.
> The first two arguments to objc_msgSend() are the "receiver" (the target
> NSObject) and a "selector"; a "selector" is basically a canonicalized
> C string. The ObjC runtime contains functions which "intern" C strings
> as selectors; the CCL::@SELECTOR macro expands into code which caches the
> results of those functions in a structure (where the structure is referenced
> as a lisp constant):
>
> ? (macroexpand-1 '(@SELECTOR "title")
> (%GET-SELECTOR #S(OBJC-SELECTOR :NAME "title" :%SEL #<A Mac Pointer #x9089BD94>))
>
> The CCL::%GET-SELECTOR function returns the value of the %SEL slot (the
> canonicalized C string) if it's non-NIL; otherwise, it calls the ObjC
> runtime to obtain a canonicalized selector address and updates the %SEL
> slot before returning it.
>
> The remaining arguments to the EXTERNAL-CALL to objc_msgSend() are
> type-keyword/value pairs (none in this example). If all of this information
> is correct, the foreign function call will return the correct value; if
> it isn't, you're likely to crash spectacularly. The SEND macro makes
> it much more likely that the number and types of foreign arguments and
> the type of the return value are correct, and you generally would only
> want to use OBJC-MESSAGE-SEND in unusual cases (there are a few such
> cases in the examples directory.)
>
> OBJC-MESSAGE-SEND only really works if the name of the selector and
> the types of the arguments/return value are known at macroexpand time;
> in general, a foreign function call of any sort works better if it
> can be compiled than it does if it has to be "interpreted" at runtime.
> There is a function - CCL::%SEND - that performs ObjC message sends
> at runtime. A Lisp rule of thumb says "if you find yourself calling
> EVAL at runtime, you're probably doing something wrong". Like most
> rules of thumb, that's not -always- true, but it's probably true
> often enough to be good advice. I think that the same advice would
> likely be useful at least as often in the case of %SEND (though,
> like EVAL, it might also be fair to say that when it -is- the right
> thing, it's often better than the alternatives.)
>
I was trying to avoid eval for just that reason but finally did get it
working. %send does the trick and sounds like the best approach given
what I'm trying to do.
More information about the Openmcl-devel
mailing list