[Openmcl-devel] Sending a message to a Cocoa object using parameters

Gary Byers gb at clozure.com
Sat Apr 8 08:52:34 UTC 2006

? (in-package "CCL")	; my init file put me in the CCL package.  Sorry.

? (macroexpand-1 '(send (the ns:ns-window w) 'title))

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)

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.)

On Sat, 8 Apr 2006, Phil wrote:

> I'm looking to achieve a level of indirection with send but not getting
> there.  What need is a way to accomplish (ccl::send my-cocoa-obj
> my-cocoa-message) where my-cocoa-obj and my-cocoa-message are strings or
> symbols that I actually want to use such as 'mywind and 'title.  Any
> suggestions on how to best accomplish this?
> Thanks,
> Phil
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list