Callback to Perl interpreter in C--something simpler than XS?

Discussion in 'Perl Misc' started by Kevin Walzer, Jan 25, 2013.

  1. Kevin Walzer

    Kevin Walzer Guest

    I'm trying to implement a Perl interface to a subset of Apple's
    AppleEvent framework, allowing a Perl app to respond to AppleScript
    commands. CPAN has a module devoted to this, but it is part of a larger
    framework that is suffering from bit-rot, has large swaths of deprecated
    and obsolete API calls, and won't build under 64-bit.

    I'm moving right along with the AppleEvent / C API stuff, but I'm not
    quite clear on how to convert this vanilla C code into something that
    Perl can interface with. What I want to do is to pass a subroutine name
    as a string to the Perl interpreter that the interpreter can run.

    Here's one example C function:

    //Handler for AppleEvents
    static OSErr AEScriptsAEHandler(const AppleEvent *theAppleEvent,
    AppleEvent *reply, long refCon) {

    OSErr err = noErr;
    AEDesc returnData;
    AEEventID eventID;
    OSType typeCode;
    AEDesc directParameter;
    char *script;

    //Read the AppleEvent
    err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeUnicodeText,

    //get event ID to look up in CFDictionary
    err = AEGetAttributePtr(theAppleEvent, keyEventIDAttr, typeType,
    NULL, &eventID, sizeof(eventID), NULL );

    //get direct parameter
    err = AEGetKeyDesc(theAppleEvent, keyDirectObject, typeType,

    CFStringRef scriptName;
    stringeventID = UTCreateStringForOSType(eventID);
    scriptName = CFDictionaryGetValue(aeDict, stringeventID);

    CFIndex length = CFStringGetLength(scriptName);
    CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length,
    script = (char *)malloc(maxSize);
    CFStringGetCString(scriptName, script, maxSize, kCFStringEncodingUTF8);

    //actually run the script
    err = ExecuteScript(script, NULL);

    What this code does is check a CFDictionary ref for a value that
    corresponds to the ID of a specific Apple event ("script"), and my idea
    is to pass the "script" string to a function called "ExecuteScript" that
    would incorporate the Perl interpreter. Is there a simpler way to
    implement this than wading through all the XSUB goo?

    In Tcl's C API it's simple as initializing a Tcl interpreter:

    static Tcl_Interp *myInterp;

    and then calling this function:

    Tcl_Eval(myInterp, script);

    In looking at the XS bits, am I missing something simpler that might
    serve my needs? The idea here is that I'm passing a single subroutine
    call to the Perl interpreter, then I'll be returning the subroutine's
    output back to the AppleEvent C API as a string. There aren't a lot of
    hooks or complex data structures that need to be addressed: I just want
    to have this as a loadable module that can interact with the running

    Thanks for any advice,
    Kevin Walzer, Jan 25, 2013
    1. Advertisements

  2. Kevin Walzer

    Kevin Walzer Guest

    On 1/26/13 5:09 AM, Ben Morrow wrote
    OK, perhaps this is the way I need to go. I'm not embedding Perl; I'm
    creating an AppleEvents wrapper that will be loaded into the Perl
    interpreter as a package.
    The AppleEvent API is a form of IPC where an app can communicate with
    another app via sending AppleEvents. The high-level interface is the
    AppleScript language. Something like this:

    tell app "Foo"
    end tell

    My Perl app will be the recipient of the "tell" command in this
    instance. My C code is intended to register the Apple Events the app
    will handle with the OS, which has to be done in C. But the actual
    execution of the code has to be done at the Perl level by the running
    interpreter. Does this make sense?

    Tcl's C API allows for easy two-way communication between the running
    interpreter and code at the C level, and it can be done so without
    threads. Here's some similar code in Tcl's C API:

    char *data = ckalloc(size + 1);
    theErr = AEGetParamPtr(event, keyDirectObject, typeUTF8Text, NULL, data,
    size, NULL);
    if (theErr == noErr) {
    tclErr = Tcl_EvalEx(interp, data, size, TCL_EVAL_GLOBAL);

    if (tclErr >= 0) {
    int reslen;
    const char *result =
    Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &reslen);

    if (tclErr == TCL_OK) {
    AEPutParamPtr(reply, keyDirectObject, typeChar, result, reslen);
    } else {
    AEPutParamPtr(reply, keyErrorString, typeChar, result, reslen);
    AEPutParamPtr(reply, keyErrorNumber, typeSInt32, (Ptr) &tclErr,

    The TclEvalEx() call executes the Tcl script, and the output is obtained
    at the C level by the Tcl_GetStringFromObj(Tcl_GetObjResult(interp),
    &reslen) call, and then passed back along the AppleEvents mechanism

    It seems that Perl has two separate API's for interacting with C, rather
    than one.

    I'll dig deeper into the XS API and see if I can make that dance the way
    I want to. I don't want to start a separate Perl interpreter, but rather
    use the main one I have. The callback part is what's leading me in this
    direction; otherwise I'd just use SWIG.

    Other suggestions, anything I've missed, are appreciated.

    Kevin Walzer, Jan 26, 2013
    1. Advertisements

  3. Kevin Walzer

    Kevin Walzer Guest

    I think I'm going to take a different tack here: ActiveState's Tkx
    module, which allows you to put a Tk interface on a Perl app, also
    includes fairly complete integration with the Tcl interpreter. I have a
    working Tcl AppleEvent implementation in my apps written in Tcl, and I
    can probably access that from Perl, and even use Perl callbacks, via the
    Tkx module. I had thought this would involve a lot of overhead and
    wanted to see if I could get a Perl-native implementation going, but it
    seems too convoluted for me. Thanks for your time.

    Kevin Walzer, Jan 28, 2013
  4. Kevin Walzer

    greymaus Guest

    Man2Doctor "When I hold my arm this way, I get an incredible pain"
    Doctor2Man "Well, dont hold your arm that way"
    greymaus, Jan 28, 2013
  5. That's IMHO an impression you mostly got because you were seeking for
    the Tcl-interface you're familiar with in Perl --- but the Perl
    interface is different. In the following, I'm going to assume that the
    AppleScript event handler is registered with some Apple library and
    then called from the library as part of some kind of 'event loop'
    processing. If this is so, the way to integrate this with perl would

    - provide an initialization function callable from perl via
    XS which performs any initialization which may be necessary
    and enables the caller to register a Perl subroutine
    supposed to handle the events

    - provide a second perl-callable function which enters the
    event loop

    The C event handler routine would then need to invoke the Perl
    callback (=> perlcall) and pass the results back to the library after
    the Perl program called the init rouine and entered the event loop.

    That's not really difficult and while XS seems somewhat dauting at a
    first glance, for simple cases, like the one you have here, declaring
    the signatures of the Perl entry points should be sufficient. As a
    contrived example and without the boilerplate, for the init routine,
    this could look like this:

    SV * subref

    which declares an XS routine returning an int to Perl and taking a
    single argument which will be a pointer to a 'scalar value' (SV). The
    actual code will then be generated by the xsubpp preprocessor. The
    h2xs program is also very helpful to 'jumpstart' a new extension
    Rainer Weikusat, Jan 28, 2013
  6. Son2Father: Whenever I try to stand up and walk, I fall down!
    Father2Son: You're only seventeen, kid, and should really stay away
    from this adult stuff for somewhat longer. While it won't help you
    stay young, it might even make you feel as if you remained a happy
    toddler for some years to come!
    Rainer Weikusat, Jan 28, 2013
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.