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

Hello esteemed monks,

I'd like to propose you an interesting challenge. I've never been an XS expert. I'm moving my first steps into these twisty little passages.

I'm coding an XS function that takes an array reference as input and returns a scalar.

Example:

my @list = ('a', 'b', 1, [1,2,3], 'c', {z=>1,y=>2}); print myfunc(\@list); # prints 'a,b,1,ARRAY(1-2-3),c,HASH(z1-y2)';

I came up with the following code that I attach here. I'm missing two main parts.

1) If nth element of list is an arrayref $r, replace $r in the original list with @$r.
2) If nth element of list is an hashref $r, append result of perl function F($r) to the result scalar string.

Can you help me?
Code follows:

#include "EXTERN.h" #include "perl.h" #include "XSUB.h" MODULE = html::BaseTag PACKAGE = html::BaseTag PREFIX = html_ +BaseTag_ void html_BaseTag_contentToString_xs(list) AV* list PREINIT: SV* val; SV* ref; SV** list_entry; SV* result; I32 alen = 0, i = 0; PPCODE: /* Initialize result scalar string */ result = newSVpv("", 0); /* Check if list is a valid array and is not empty */ if( list && (alen = av_len(list)) > -1 ) { /* Iterate through the list */ /* fprintf(stderr, " length of list is %d\n", alen); */ for(; i <= alen; i++) { list_entry = av_fetch(list, i, 0); if( list_entry ) val = *list_entry; /* Check if array element is a reference */ /* fprintf(stderr, " type of SV = %d\n", SvTYPE(val)); */ if( SvROK(val) ) { /* Get the corresponding reference value */ ref = SvRV(val); /* Type is array ref */ if( SvTYPE(ref) == SVt_PVAV ) { sv_catpv(result, "*AREF, "); /* XXX splice(@list, i, 1, @ref) */ } /* Type is hash ref */ else if( SvTYPE(ref) == SVt_PVHV ) { sv_catpv(result, "*HREF, "); /* XXX result .= html::BaseTag::tagToString(ref); */ } } else if( SvPOK(val) ) { /* fprintf(stderr, " array element %d is %s\n", i, Sv +PV_nolen(val)); */ sv_catpvn(result, SvPVX(val), SvCUR(val)); sv_catpv(result, ", "); } } sv_catpv(result,"\n"); } EXTEND(SP,1); PUSHs(sv_2mortal(result));

Replies are listed 'Best First'.
Re: XS: Converting a data structure in a string
by creamygoodness (Curate) on Nov 04, 2005 at 16:57 UTC

    There's a conceptual flaw in your sub. The splice would increase the number of elements in the array, but your alen variable isn't going up, so there will be a number of elements you never get to.

    Let's start by translating your function into a pure Perl version that does what you want. I'm going to ignore package considerations for the present.

    sub contentToString { my $list = shift; my $result = ''; if (ref($list) eq 'ARRAY' and @$list) { my $i; while ($i < $#$list) { my $list_entry = $list[$i]; if (ref($list_entry)) { if (ref($list_entry) eq 'ARRAY') { splice(@list, $i, 0, @$list_entry); } elsif (ref($list_entry) eq 'HASH') { $output_string .= tagToString($list_entry); } } else { $result .= $list_entry; } $i++; } } return $result . "\n"; }
    1) If nth element of list is an arrayref $r, replace $r in the original list with @$r.

    Is there a possibility that the data structure will be more than two levels deep? If so, you'll need a recursive function. UPDATE: Oh wait, as I look at the Perl function again, I see that it will work, and you don't need recursion.

    2) If nth element of list is an hashref $r, append result of perl function F($r) to the result scalar string.

    You're going to need to take a look at the perlcall docs in order to figure out how to make callbacks from XS to Perl work. It's a little complicated because you need to put in some stack manipulation stuff that xsubpp normally does behind the scenes for your XSUB.

    --
    Marvin Humphrey
    Rectangular Research ― http://www.rectangular.com
      There's a conceptual flaw in your sub. The splice would increase the number of elements in the array, but your alen variable isn't going up, so there will be a number of elements you never get to.
      The original perl function already works as you wrote. My XS is only a "sketch" version now and does not handle this "problem".
      Is there a possibility that the data structure will be more than two levels deep? If so, you'll need a recursive function.

      As you noted, this is not necessary.
      The recursive solution was our first perl version. Then I wrote a non-recursive version that cuts down stack usage by a good factor. This is the actual function that I want to rewrite in XS.

      You're going to need to take a look at the perlcall docs

      Thanks for your suggestion.

Re: XS: Converting a data structure in a string
by tirwhan (Abbot) on Nov 04, 2005 at 16:27 UTC

    I'm not an expert on this at all, but wouldn't you be better off using Inline::C (especially as you're just starting out)? Looks like a considerably lower entry barrier for interbreeding C and perl.


    Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian W. Kernighan
      I'm not sure this algo is a good candidate for either XS or Inline::C. It's not computationally intensive. There might be a small boost in performance from going to XS, but unless this particular sub has been identified as a bottleneck during profiling and you need to squeeze every last CPU cycle out of it, I'd leave it in Perl.
      --
      Marvin Humphrey
      Rectangular Research ― http://www.rectangular.com

        Agreed. I was under the impression the OP was trying to dip his toes into mixing C with perl code, and personally the only approach to that which doesn't send me slinking away shamefully is with Inline::C ;-)


        Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian W. Kernighan
        Thanks for your suggestion.

        This function is already implemented at perl level, and for historical reasons (read: legacy code) it handles Big™ data structures, resulting in 10/15 Mb strings.

        And yes, this function has been selected as good candidate for XS/ Inline::C rewriting through basic profiling.

        I thought it might be a valid experiment to learn XS. Probably I should discard Inline::C as a viable alternative because of internal distribution and servers update issues. Nevertheless I'm going to study Inline::C some more...