Question

Say you want to open a simple alertbox, which in the objective-c universe would be something like:

NSAlert *alert = [[[NSAlert alloc] init] autorelease];
    [alert setMessageText:@"Alert."];

    [alert beginSheetModalForWindow:window
                      modalDelegate:self
                     didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
                        contextInfo:nil];

The beginModalForWindow is defined as a selector method. In apples reference guide it's full name is "beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:"

It's defined in NSAlert.h as:

- (void)beginSheetModalForWindow:(NSWindow *)window modalDelegate:(id)delegate didEndSelector:(SEL)didEndSelector contextInfo:(void *)contextInfo;

Now the simple question, how to define this method in ruby ffi?

module AppKit
  extend FFI::Library

  # Load the Cocoa framework's binary code
  ffi_lib '/System/Library/Frameworks/AppKit.framework/AppKit'

  attach_function :beginSheetModalForWindow, [:pointer,:pointer,:pointer], :bool
end

Fails with:

An exception occurred running ffi-test.rb
  Function 'beginSheetModalForWindow' not found in [/System/Library/Frameworks/AppKit.framework/AppKit] (FFI::NotFoundError)
Was it helpful?

Solution

In short, you can't. At least not without a slew of hoop jumping.

attach_function does what it says; it binds a C function into the Ruby runtime. beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo: is not a C function; it is a selector.

What you really want to bind is the implementation of that selector.

But not really.

What you'd really want to bind is objc_msgSend with a type signature that includes all the arguments to that method. And you'll also need to attach sel_getUid. Oh, and you'll need to attach objc_lookUpClass

Then you would do something like (pseudo code):

 ... attach objc_msgSend to msgSend with no arguments and object return type ...
 alert = msgSend(msgSend(msgSend(lookupClass("NSAlert"),getUid("alloc")),
           getUid("init")), getUid("autorelease"))

 ... attach objc_msgSend to bs with all the arguments for beginSheetModal....
 bs(alert, getUid("beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo", ... all the arguments ...))

Or something like that.

At which point, you've re-invented a very rudimentary form of MacRuby or RubyCocoa.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top