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

K

Kevin Walzer

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,
&returnData);

//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,
&directParameter);

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

CFIndex length = CFStringGetLength(scriptName);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length,
kCFStringEncodingUTF8);
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
interpreter.

Thanks for any advice,
Kevin
 
K

Kevin Walzer

On 1/26/13 5:09 AM, Ben Morrow wrote
...and now you've gone back to talking about extending perl again. I'm
confused. How does your program start? Do you want to start in Perl,
call into C, and then call back into Perl again? That's entirely
possible, but you have to use both the perlxs and perlcall facilities.

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.
However, that C above looks to me as though it's a callback from some
Apple facility. How does that get invoked? If it gets called
asynchronously, while there is other Perl code being executed, you will
probably need to use the ithreads mechanism to prevent the two bits of
Perl from interfering with each other.

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"
dosomething()
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,
sizeof(int));
}

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

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.

Thanks,
Kevin
 
K

Kevin Walzer

I would recommend staying away from SWIG, at least for perl. I've never
seen a SWIG-generated perl extension that was anything other than
painful to use; IMHO it's trying too hard to provide a cross-interpreter
interface, and the various interpreters' APIs aren't really compatible
enough for that to work. A basic XS interface that doesn't do anything
clever is just a matter of going through and listing the functions
involved.

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
 
G

greymaus

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

Man2Doctor "When I hold my arm this way, I get an incredible pain"
Doctor2Man "Well, dont hold your arm that way"
 
R

Rainer Weikusat

Kevin Walzer said:
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.

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
involve:

- 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:

int
event_library_init(subref)
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
module.
 
R

Rainer Weikusat

greymaus said:
Man2Doctor "When I hold my arm this way, I get an incredible pain"
Doctor2Man "Well, dont hold your arm that way"

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!
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,739
Latest member
Clint8040

Latest Threads

Top