Temeschwarrior has asked for the wisdom of the Perl Monks concerning the following question:

Hi com,
I use perl 5.8.8 and iam writing an perlxs interface for a few Shared librarys
Even when i try to uses the default typemap for char ** i will get an error message like this:

perl: symbol lookup error: /home/My/blib/arch/auto/My/My.so: undefined symbol: XS_unpack_charPtrPtr

what does that mean?
here the code:
void my_func(string, intager, strings) const char * string int intager char ** strings

Replies are listed 'Best First'.
Re: char ** typemap in perlxs
by rafl (Friar) on Apr 16, 2008 at 16:21 UTC

    The default typemap maps char ** to T_PACKEDARRAY. The input typemap for that looks like this:

    T_PACKEDARRAY $var = XS_unpack_$ntype($arg)

    so to convert the perl argument to a char ** value it will call XS_unpackcharPtrPtr. However implementing this function is something you will need to do as xsubpp can't know what the meaning of the data is.

    The following (untested) code might be useful if you want to pass an array reference from perl and your c code expects a NULL terminated list of strings:

    char ** XS_unpack_charPtrPtr (SV *arg) { char **ret; AV *av; I32 i; if (!arg || !SvOK (arg) || !SvROK (arg) || (SvTYPE (SvRV (arg)) != + SVt_AV)) { croak ("array reference expected"); } av = (AV *)SvRV (arg); ret = (char **)malloc (av_len (av) + 1); for (i = 0; i <= av_len (av); i++) { SV **elem = av_fetch (av, i, 0); if (!elem || !*elem) { croak ("foo"): } ret[i] = SvPV_nolen (*elem); } ret[i + 1] = NULL; return ret; }

    As this function allocates memory you will need to free the return value when you don't need it anymore. The CLEANUP section of an xsub might be a good place for that.

Re: char ** typemap in perlxs
by syphilis (Archbishop) on Apr 17, 2008 at 02:49 UTC
    Looking at rafl's explanation, I think I find it much simpler to avoid the char** typemapping altogether, and pass the argument from perl to C as an AV* rather than a char**

    Here's a self-contained ultra-simplistic example (plagiarised from "Extending and Embedding Perl"):
    use warnings; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; void foo(AV * avref) { int i, len; SV ** elem; len = av_len(avref) + 1; for(i = 0; i < len; i++) { elem = av_fetch(avref, i, 0); printf("%s\n", SvPV_nolen(*elem)); } } EOC @strings = ( 'hello', 'a string', 'another string', 'bye', ); foo (\@strings);
    Not sure if that helps at all ... I concede that it certainly doesn't answer the question you asked :-)

    Cheers,
    Rob

      I'd do something similar but I'd write the "translate Perl data structure into C data structure" code in Perl not in XS. I'd much rather have 6 lines of advanced Perl code than 6 lines of basic XS code (as "basic" as XS code gets). For one, making adjustments to XS code requires compiling and installing a new shared library while limitations or bugs in Perl code can be worked around in so many ways. Just understanding what the XS code is really doing can be quite a challenge while understanding Perl code is something that so many more people are adept at and where you have tons of tools available to aid you (lots of simple ways to "debug" what is going on).

      Then there is the fact that dealing with Perl data structures in XS code almost always results in very brittle code (and quite often results in downright buggy code). Your XS code won't handle a tied or overloaded or otherwise magic array of strings while my Perl code will.

      sub my_func { my( $string, $int, @strings )= @_; my $ptrs= pack "P" x @strings, @strings; $ptrs .= pack "LL", 0, 0; # For odd systems with sizeof(IV) < size +of(void *) _my_func( $string, $int, $ptrs ); } # and void _my_func( st­ring, integer, strings ) const char * string int integer char * strings CODE: my_func( string, integer, (char **)(strings) );

      I find it rather stupid to map "void *" the way the standard typemap does so I used "char *" above.

      But the trick that remains is all of the details about lifespan of the data (and other memory allocation traps). The above example works fine so long as the C my_func() just reads the data passed to it and doesn't try to reallocate anything or keep pointers to parts of it to be used at some later date or such. An argument description of just "char ** string" tells very little about those details and those details can completely change how such an argument needs to be handled.

      - tye