tags:

views:

125

answers:

2

Both perlcall (in the "Strategies for storing Callback Context Information" section) and Extending and Embedding Perl (in the "Callback" section) lists 3 different ways to handle calling Perl sub routines from XS/C:

  1. Immediately: XS calls
  2. Deferred: saving the sub ref as an SV* for later
  3. Multiple: save n sub refs for later

The example and details listed for #3 above use a hash in XS to associate the sub ref with a particular C function, but they predefine a fixed number of C functions which is not adequate.

I'm working on an XS interface to a C library that uses callbacks/function pointers with optional arguments e.g.:

  blah(custom_type *o, void (*func) (void *data, int more_data), const void * data);

The C blah in this library will end up calling the function passed to it along with the data passed in.

If possible, I'd like to do a 1-to-1 mapping of the C API to the Perl one. e.g.

  blah($o, \&func, $data);

Currently, I have #2 above, but another call to blah() would overwrite the saved SV *.

How would I implement #3 above?

A: 

I can't give you a complete answer, unfortunately. Instead, let me give you a few pointers:

  • You can't map a Perl code reference (technically a CV*) to a C function. Perl code references are really data (a tree of OPs) with some function pointers attached for doing the specific bits of work in each OP. (Cf. perl guts illustrated for details on OP structures).

  • What you may be able to get away with is write your own C function with the required signature, pass it to the C-level blah. Then in your XS code, get the Perl callbacks (which gets passed in as a Perl scalar (SV)). See "perldoc perlapi". Look for svtype and the SVt_* bits.) Store those SVs somewhere. Then, when you want the C library to do the callbacks, your C callback gets control and can dispatch to your Perl-level callbacks. Cf. "perldoc perlcall".

Good luck. You're about to do strange things.

tsee
Also, let me add that my answer pertains to what I think you're trying to do. I am not 100% convinced I understood your goals correctly. (Cf. ysth's comment on your question.)
tsee
A: 

This is the solution I came up with:

Most of the callbacks in this C library will take a user supplied void * and pass that as the first argument. So I save the SV * and user supplied data in a struct:

typedef struct __saved_callback {
    SV   *func;
    void *data;
} _saved_callback;

My XS function will allocate a _saved_callback struct and pass that as the first argument to call_perl_sub() with the Perl sub reference and that user supposed data.

void
blah(obj, func, data)
    whatever *obj
    void *func
    void *data
    CODE:
        _saved_callback *sc = NULL;
        Newx(sc, 1, _saved_callback);
        sc->func = (SV *)func;
        sc->data = data;
        blah(obj, call_perl_sub, sc);

Then call the Perl sub reference (I've omitted the stack manipulation for the user supplied data argument):

void call_perl_sub(void *data) {
    dSP;
    int count;
    _saved_callback *perl_saved_cb = data;

    count = call_sv(perl_saved_cb->func, G_DISCARD);
    if ( count != 0 )
        croak("Expected 0 value got %d\n", count);
}
Adam Flott
Note: I'm sure there is a better solution, but I'm still learning XS.
Adam Flott