Call a perl object method from Obj-C

Sam Alcoff
  • Sam Alcoff
    Sam Alcoff

        I spent a couple of hours today playing with CamelBones -- very nice.

        One question though.  Instead of creating my whole application in Perl, I've been messing with just delegating portions.  What I haven't figured out how to do yet, is from Objective C, call a Perl Object's method using a CBPerlObject.  Can this be done at this point?

    Sample code:

    <CamelBones Perl interp init code...>

    myDel = [CBPerlObject namedObject:@"perlCtrl" ofClass:@"perlCtrl"];

    [myDel refresh];

    I end up with a compiler warning:
    perlCtrl.m:24: warning: `CBPerlObject' does not respond to `refresh'

    There's a sub defined in called "refresh".

    This warning makes sense if there's no built-in delegation.  Is there?  If not, how would I do something like this?

    • Sherm Pendley
      Sherm Pendley

      You can call object methods, but constructors aren't yet supported. You can create an object by eval-ing a line of Perl code, then get a proxy object with namedObject: though.

      I haven't documented it yet, but basically what you do is:

      CBPerlObject *myObject;
      CBPerl *sp = [[CBPerl alloc] init];
      [sp useModule: @"Foo::Bar"];
      [sp eval: @"$foo = new Foo::Bar"];
      myObject = [sp namedObject: @"foo"];

      After that, you can call methods on myObject just like you would a "normal" Objective-C object. And, of course, you'll probably want to keep the existing call to NSApplicationMain(), instead of replacing it with a call to CBApplicationMain().

      Get/set "magic" works, too, if your Perl object is implemented as a blessed hash. If you call a method that doesn't exist, but a property by that name exists in the hash, the property is returned. If you call a "setFoo:" method, and the property "Foo" exists (it won't create it), it's value is set to the new value.

      The only parameter/return type currently supported is id. NSStrings are automatically converted to Perl scalars; any other object type is "wrapped" so that you can call its methods from Perl.

      • Sherm Pendley
        Sherm Pendley

        Continuing my previous response...

        When calling Perl methods from Objective-C, colons are replaced with underscores, and the trailing underscore, if there is one, is removed. (I'm told that other Perl/ObjC bridges exist, and that they require the trailing underscore, so support for that will is planned.)

        So, if you have the Perl method takeThis_andThat:

        sub takeThis_andThat {
            my ($this, $that) = @_;

        You'd call it like this:

        [myObject takeThis:this andThat:that];

        • Sam Alcoff
          Sam Alcoff

          Very good.  I'd had a bit of trouble with properly initializing the Perl interpreter at an earlier point in the code, but that's probably due to my lack of experience with Obj-C.

          Anyway, following up on your earlier posts, I'm getting a sigbus when I try to set an attribute of  a perl object via the "setFoo" method call you described.

          Below is some sample code which causes the error.  I'm also not sure why I have to reaquire a handle to the delegate object in the "refresh" Obj-C code, but again, I probably don't understand the memory management in Obj-C yet.

          @implementation perlCtrl

          - (id)init
              if (self = [super init]) {
                  perl = [CBPerl sharedPerl];
                  NSLog(@"Init Perl Delegate Object");
                  [perl useModule: @"perlCtrl"];
                  [perl eval: @"$perlCtrl = new perlCtrl"];
                  myDel = [perl namedObject: @"perlCtrl"];
              return self;

          - (IBAction)refresh:(id)sender
              NSLog(@"Outer Refresh call");
              myDel = [perl namedObject: @"perlCtrl"];
              [myDel setTextField:textField];
              [myDel refresh:sender];


          The code works fine without the call to setTextField.  The perlCtrl object (in perl) is implemented as a blessed hashref, with a key "TextField" created in the call to new.

          BTW - The ultimate goal of my hacking is to create some sort of Obj-C proxy controller, so you can use Interface Builder with CB.  

          Thanks, for your help.

          • Sherm Pendley
            Sherm Pendley

            You *can* use Interface Builder! :-)

            ShuX was built with IB - it's a minimal interface, but it's stored in a secondary NIB and loaded in response to applicationDidFinishLaunching: by the shared application object's delegate.

            IB is the reason that setFoo: is supported. When a NIB is loaded, setFoo: is sent to the NIB's owner for each named outlet. It uses the outlet name as the name of the property to set, and passes a pointer to the deserialized control that was loaded from the NIB.

            About memory management: By tradition, Objective-C objects created with init* and copy are retained, but objects returned from class methods are autoreleased; that is, they're automatically deallocated at the end of the current event. That, I think, is what's causing your sigbus - when the CBPerl object in which they exist disappears, it leaves the CBPerlObject pointing to deallocated memory. That's why you couldn't use it in refresh:.

            Because the CBPerl object had been released, a new one is automatically created when you call namedObject:. But, that named object does not exist in the new Perl context - it's just been created - so namedObject: returns nil. Objective-C is still C in many respects - and one is that nil pointers are still trouble.

            For applications in which a long-running Perl context is important, you should always use [[CBPerl alloc] init] at least once, to create a retained CBPerl object. After that, it's safe to use [CBPerl sharedPerl] to get a pointer to the shared CBPerl object.

            Other applications may want a clean Perl context for each of a series of scripts. Plugins or macros, for instance, could be run in the context of a Perl object that only exists for the lifetime of a single invocation of the macro.

        • Keith Anderson
          Keith Anderson

          Does that mean that in order to pass multiple arguments from ObjC to Perl objects the Perl methods MUST be named similarly to ObjC methods?

          i.e. - You can't call Perl objects of the form:
            sub SomeMethod{
               my $self = shift;
               my $arg1 = shift;
               my $arg2 = shfit;

          without rewriting them to be:
            sub SomeMethodArg1_Arg2{

          The idea being to use existing Perl modules (CPAN and homegrown) within Objective C applications without having to rewrite all of the method signatures...

    • laurens

      How can we

      1. create an NSMutable<Object> in main.m
      pass it to CBPerl
      modify this object with perl code
      retrieve the modification in main.m

      2. conversely, create a foundation NSMutable<Object> with perl code
      retrieve the object from main.m
      modify it with objective C code
      and see the modification with perl code

      The only thing I was able to code was point 2 using PerlObjCBridge (and I am no longer a novice)