[MacRuby-devel] Using performRubySelector

Jeremy Voorhis jvoorhis at gmail.com
Thu May 28 11:02:17 PDT 2009


I'd love to do it in FFI, but getting this far in C was a lot of work!

... so here's another crackpot idea of mine. Another project I have
been following is the Pure Language
<http://code.google.com/p/pure-lang>. It aims to be a simple,
interpreted functional language based on term-rewriting, and also has
an LLVM JIT compiler. Using LLVM, they support a literal syntax for
declaring foreign functions.

To assist in creating wrappers for libraries, they created a tool
called pure-gen that parses C header files and dumps out a .pure file
containing FFI declarations. The tool is written in Haskell using a
module called Language.C, but I wonder if we could bootstrap something
similar by wrapping up Clang's libparse. They've managed to create
wrappers for gsl, gtk+ and some other libraries by starting with the
generated FFI decls, and adding some syntactic sugar and abstraction
within Pure as appropriate.

Implementing such a tool is probably going to take a lot of effort,
but the benefits are pretty clear – especially since generated
wrappers might be usable in JRuby and MRI. I also don't know how much
this overlaps with BridgeSupport, but I just wanted to put the idea
out there.

Best,

Jeremy

On Thu, May 28, 2009 at 10:38 AM, Matt Aimonetti
<mattaimonetti at gmail.com> wrote:
> I second what Jeremy said. A nice Midi wrapper would be great.
> Jeremy, regarding your MusicPlayer lib, the other option is to port it to
> FFI and therefore making it JRuby and MacRuby compatible.
>
> - Matt
>
> On Thu, May 28, 2009 at 10:32 AM, Jeremy Voorhis <jvoorhis at gmail.com> wrote:
>>
>> Mike,
>>
>> Is your code available anywhere, e.g. on github? I haven't looked at
>> the PyObjC code myself, but I'd be interested in having really decent
>> CoreMIDI support available in MacRuby.
>>
>> Btw, when C extensions are supported, simple apps will be able to use
>> my MusicPlayer lib at
>> http://github.com/jvoorhis/music_player/tree/master, which wraps much
>> of the MusicPlayer/MusicSequence api in AudioToolbox.framework. It's
>> short on documentation, but it's easy to get up and running on MRI
>> 1.8.x or 1.9.1.
>>
>> Best,
>>
>> Jeremy
>>
>> On Wed, May 27, 2009 at 11:25 PM, Mike Laurence <mklaurence at gmail.com>
>> wrote:
>> > Hmmm... I'm doing several things different, perhaps one or more of
>> > them are impossible :-)
>> >
>> > Basically, I'm trying to get MIDI support into Ruby via the PYMIDI
>> > obj-c library, which is really just a wrapper around CoreMIDI. One way
>> > I had thought of: create an additional obj-c class (MidiReceiver)
>> > which processes incoming packets for a given MIDI source and then call
>> > methods such as "note", "controlChange", "clockTick", etc. This
>> > MidiReceiver class can then be overridden by a custom ruby class that
>> > contains those same methods.
>> >
>> > Here is a quick version of the MidiReceiver class (with only the
>> > clockTick method for simplicity):
>> >
>> > ------------- MidiReceiver --------------
>> >
>> > #import <MacRuby/MacRuby.h>
>> > #include <CoreMIDI/CoreMIDI.h>
>> >
>> > @interface MidiReceiver : NSObject
>> >
>> > - (void) processMIDIPacket: (MIDIPacket*) packet;
>> > - (void) clockTick;
>> >
>> > @end
>> >
>> > @implementation MidiReceiver
>> >
>> > - (void) processMIDIPacket: (MIDIPacket*) packet {
>> >    if (packet->length > 0) {
>> >
>> >        int statusByte = packet->data[0];
>> >        int status = statusByte >= 0xf0 ? statusByte : statusByte >> 4 <<
>> > 4;
>> >
>> >        switch (status) {
>> >        case 0x90: // Note on, etc...
>> >        case 0xf8: // Clock tick
>> >            [self performRubySelector:@selector(clockTick)];
>> >            break;
>> >        }
>> >    }
>> > }
>> >
>> > - (void) clockTick {
>> > }
>> >
>> > @end
>> >
>> > --------------------------------------------------------
>> >
>> > Then, I have a ruby subclass of MidiReceiver that overrides clockTick,
>> > etc.:
>> >
>> > class LiveMidiReceiver < MidiReceiver
>> >  def clockTick
>> >    puts "tick!"
>> >  end
>> > end
>> >
>> > And then, in my Ruby ApplicationController, I'm finding the MIDI
>> > source and adding an instance of LiveMidiReceiver as a MIDI receiver:
>> >
>> > @src = PYMIDIManager.sharedInstance.realSources.find{ |s|
>> > s.displayName == 'KONTROL49 PORT A' }
>> > receiver = LiveMidiReceiver.new
>> > @src.addReceiver(receiver)
>> >
>> > The LiveMidiReceiver instance, upon receiving a midi packet, is called
>> > properly up to the point of the performRubySelector, but thereafter it
>> > launches into the debugger with EXC_BAD_ACCESS messages or other
>> > unsightly stack dumps.
>> >
>> > ---
>> >
>> > An even *better* interface would be to have the "clockTick" and other
>> > calls be performable on an arbitrary ruby object without having to
>> > subclass MidiReceiver (e.g., have MidiReceiver send "clockTick", etc.
>> > to a delegate object which has been created solely in Ruby). I tried
>> > that and it gave me similar results, although strangely it only
>> > crashed the first time on a clean build - thereafter I saw no crashes,
>> > but still no confirmation of ruby method calls either.
>> >
>> > To test that, I just added a delegate object to MidiReceiver, and then
>> > I changed the clockTick recipient from self to delegate:
>> >
>> > [delegate performRubySelector:@selector(clockTick)];
>> >
>> > Then set receiver.delegate = self in my ApplicationController. I'll
>> > bet I need some more hooks than that, although it sure would be nice
>> > to send ruby messages from obj-c willy-nilly :-)
>> >
>> > ---
>> >
>> > I hope I'm making some sense here! I greatly appreciate any info that
>> > you can send my way. Hopefully when I get this all figured out I can
>> > write a nice, fat blog post about it :-)
>> >
>> > Regards,
>> > Mike Laurence
>> >
>> > On Wed, May 27, 2009 at 8:18 PM, Laurent Sansonetti
>> > <lsansonetti at apple.com> wrote:
>> >> Hi Mike,
>> >>
>> >> On May 26, 2009, at 5:45 PM, Mike Laurence wrote:
>> >>
>> >>> Hello,
>> >>>
>> >>> I'm trying to get some obj-c code to talk back to my ruby. After
>> >>> encountering some "EXC_BAD_ACCESS" messages and scouring the web, I've
>> >>> concluded that I'm probably supposed to use performRubySelector
>> >>> instead of just expecting selectors to work when overridden by ruby
>> >>> subclasses, etc.
>> >>>
>> >>> What is the preferred way to get this to work? I looked through some
>> >>> of Elysium's old code (which used performRubySelector), but I'm having
>> >>> trouble wrapping my head around how you're supposed to use the MacRuby
>> >>> sharedRuntime to get things to happen. If someone could give me a
>> >>> quick example of how to call arbitrary ruby methods, I would highly
>> >>> appreciate it.
>> >>>
>> >>> Of course, if I'm completely off base and there's some other way to
>> >>> call ruby code, please let me know!
>> >>
>> >> Calling Ruby from Objective-C can be problematic, depending if you want
>> >> to
>> >> call a pure Ruby method or a Ruby method that overrides an Objective-C
>> >> one.
>> >> If you want to dispatch the method by yourself (and if it's a Ruby
>> >> method
>> >> that overrides a specialized Objective-C method), you may want to be
>> >> very
>> >> careful about the types of the arguments you are passing to, as well as
>> >> the
>> >> return value.
>> >>
>> >> In any case, using performRubySelector: is better because arguments
>> >> will be
>> >> converted from Objective-C objects (id) into the expected type, and the
>> >> return value will be converted into an Objective-C object. Also,
>> >> performRubySelector: can deal with Ruby methods that have optional or
>> >> splat
>> >> arguments.
>> >>
>> >> $ cat t.m
>> >> #import <Foundation/Foundation.h>
>> >> #import <MacRuby/MacRuby.h>
>> >>
>> >> @interface Foo : NSObject
>> >> @end
>> >>
>> >> @implementation Foo
>> >> - (int)aMethodReturningInt
>> >> {
>> >>    return 123;
>> >> }
>> >> @end
>> >>
>> >> int main(void)
>> >> {
>> >>    [[MacRuby sharedRuntime] evaluateString:@"class X; def foo(x=1, *a);
>> >> p x,
>> >> a; end; end"];
>> >>    Class k = NSClassFromString(@"X");
>> >>    id o = [k new];
>> >>
>> >>    [o performRubySelector:@selector(foo)];
>> >>    [o performRubySelector:@selector(foo:) withArguments:@"1", NULL];
>> >>    [o performRubySelector:@selector(foo:) withArguments:@"1", @"2",
>> >> @"3",
>> >> NULL];
>> >>
>> >>    [[MacRuby sharedRuntime] evaluateString:@"class Bar < Foo; def
>> >> aMethodReturningInt; 42; end; end"];
>> >>    k = NSClassFromString(@"Bar");
>> >>    o = [k new];
>> >>
>> >>    NSLog(@"%d", [(Foo *)o aMethodReturningInt]);
>> >>    NSLog(@"%@", [o
>> >> performRubySelector:@selector(aMethodReturningInt)]);
>> >>
>> >>    return 0;
>> >> }
>> >> $ gcc t.m -o t -framework Foundation -framework MacRuby -fobjc-gc
>> >> $ ./t
>> >> 1
>> >> []
>> >> "1"
>> >> []
>> >> "1"
>> >> ["2", "3"]
>> >> 2009-05-27 18:16:36.092 t[11922:10b] 42
>> >> 2009-05-27 18:16:36.095 t[11922:10b] 42
>> >> $
>> >>
>> >> If you still have problems, it would be easier if you could paste some
>> >> code,
>> >> this way I can try to help you more.
>> >>
>> >> HTH,
>> >> Laurent
>> >> _______________________________________________
>> >> MacRuby-devel mailing list
>> >> MacRuby-devel at lists.macosforge.org
>> >> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>> >>
>> > _______________________________________________
>> > MacRuby-devel mailing list
>> > MacRuby-devel at lists.macosforge.org
>> > http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>> >
>> _______________________________________________
>> MacRuby-devel mailing list
>> MacRuby-devel at lists.macosforge.org
>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>


More information about the MacRuby-devel mailing list