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

I'll ask it in the simplest most lame terms I can, since I am missing something very fundamental in perl

I have a C function:
double do_nothing(double x[], int sizeOfX)
this is what it does:
double do_nothing(double x[], int sizeOfX) { int index; for(index=0; index<sizeOfX; index++) { x[index]=1; } return x[0]; }

what should be my XS function??? How do I write it so the XS function takes array and a number, pass it to the C function, return the array so I can read its cell through perl?

I know it should start with this:

double do_nothing(x, sizeOFX) AV * x int sizeOfX

Please, long answers - I'm dumb. I couldn't find a single example on the internet which explains it and no reference to perlXS or perlguts documentation (I'm already reading them - all 34 pages of perlXS and 45 pages of perlguts, but I learn better from examples)

Replies are listed 'Best First'.
Re: perl XS - passing array to C and getting it back
by syphilis (Archbishop) on Mar 29, 2016 at 13:47 UTC
    double do_nothing(double x[], int sizeOfX)

    My advice is to install Inline::C and try this script:
    use strict; use warnings; use Math::BigInt; use Inline C => Config => BUILD_NOISY => 1, USING => 'ParseRegExp', CLEAN_AFTER_BUILD => 0, ; use Inline C => <<'EOC'; double do_nothing(SV * x, ...) { dXSARGS; /* enable stack manipulation */ int index; for(index=0; index<items; index++) { printf("%f ", SvNV(ST(index))); ST(index)=newSVnv(1.34); } printf("\n"); return SvNV(ST(0)); } EOC my @in = (2.3, 3.4, 4.5, 6.8); my $d = do_nothing(@in); print $d, "\n"; my @next = (-1.7, -1.3, @in); my $d2 = do_nothing(@next); print $d2, "\n"; __END__ Outputs: 2.300000 3.400000 4.500000 6.800000 1.34 -1.700000 -1.300000 2.300000 3.400000 4.500000 6.800000 1.34
    Because that script specifies CLEAN_AFTER_BUILD=>0 you can then cd into the ./_Inline/build directory and view the XS file that was autogenerated and used.
    (I've never really learned how to write XS - I just use the XS file that Inline::C creates.)

    There are of course, other ways to formulate do_nothing() - have a look at perldoc Inline::C-Cookbook for examples of the above and other perl API usage.
    And see perldoc perlapi for documentation on the API functions.

    In XS, it is allowable to pass a list of "double" types as arguments, but I think passing a variable-sized list of doubles (as you intended) would turn out to be messier than the above demo.
    And that's why I've opted to pass the values as a list of "SV*" types.

    You could also write do_nothing() as:
    double do_nothing(SV * x) { ... do stuff }
    In that case you'd be calling do_nothing() from perl as something like:
    my $double = do_nothing(\@in);
    I can give you a demo of that, too, if you like.

    As a means to understanding the perl API, I really can't recommend Inline::C strongly enough.
    It gives one the opportunity to quickly and easily trial constructs about which one is unsure - and is, IMO, the best available XS/API learning tool.

    Cheers,
    Rob
Re: perl XS - passing array to C and getting it back
by BrowserUk (Patriarch) on Mar 29, 2016 at 12:21 UTC
    return the array

    You cannot return an array from a C function. Your c code example is returning the modified value of the first element of the array passed (by reference).

    That means that in the following C calling code:

    ... double rv = 0, ary[ 10 ] = { 0, }; rv = do_nothing( ary, sizeof( ary );

    After the call, rv would contain 1.0, the modified value of ary 0 ; and ary would have been modified to contain 10 x 1.0.

    So what do you want the function -- as seen from Perl - to do?

    • Take a reference to an existing perl array and modify it in place?
    • Take a reference to an array and return a list of values derived from the contents of the passed array and modified by the C function?
    • Take a list of values, and return a list of modified values?
    • Take a reference to an array and return a reference to a new array containing the modified values?

    All of these are possible, but only the first can be written in C alone; all the others would need to be written in XS code.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.
      You are correct. I need the first option. (The return value is just for testing purposes). Pass array into C code which will manipulate its values. and then I want to be able to read those values in perl.
        I need the first option.

        Then do_nothing1() below will get you started:

        #! perl -slw use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => '_1159002', CLEAN_AFTER_BUILD => 0 +; #define IS_VARS Inline_Stack_Vars #define IS_RESET Inline_Stack_Reset #define IS_ITEMS Inline_Stack_Items #define IS_ITEM( n ) Inline_Stack_Item( n ) #define IS_PUSHS( s ) Inline_Stack_Push( sv_2mortal( s ) ) #define IS_PUSHIV( iv ) Inline_Stack_Push( sv_2mortal( newSViv( iv ) ) + ) #define IS_PUSHUV( uv ) Inline_Stack_Push( sv_2mortal( newSVuv( uv ) ) + ) #define IS_PUSHNV( nv ) Inline_Stack_Push( sv_2mortal( newSVnv( nv ) ) + ) #define IS_DONE Inline_Stack_Done double do_nothing1( AV *x, int sizeOfX ) { int index; for( index=0; index<sizeOfX; index++ ) { av_store( x, index, newSVnv( 1.0 ) ); } return SvNV( *av_fetch( x, 0, NULL ) ); } void do_nothing2( AV *a, SV *size ) { IS_VARS; int index; IS_RESET; for( index = 0; index < SvIV( size ); ++index ) { IS_PUSHNV( 1.0 ); } IS_DONE; } void do_nothing3( SV*dummy, ... ) { IS_VARS; int index; for( index = 0; index < IS_ITEMS; ++index ) { IS_ITEM( index ) = newSVnv( 1.0 ); } return; } AV *do_nothing4( AV *x ) { IS_VARS; AV *r = newAV(); int index; for( index = 0; index < IS_ITEMS; ++index ) { av_push( r, newSVnv( SvNV( *av_fetch( x, index, NULL ) ) + 1.0 + ) ); } return r; } END_C my @a = ( 0 ) x 10; print do_nothing1( \@a, 10 ); print "@a"; my @b = ( 0 ) x 10; print join ' ', do_nothing2( \@b, 10 ); print join ' ', do_nothing3( 1 .. 10 ); my @c = ( 1 .. 10 ); print join ' ', do_nothing4( \@c );

        If you have Inline::C installed and you run the above code you'll get this output:

        C:\test>1159002.pl 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

        If you then look in the _inline directory tree created below the cwd where you run this, and look for the file _Inline/build/_1159002/_1159002.xs you'll see how it all fits together:


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: perl XS - passing array to C and getting it back
by salva (Canon) on Mar 29, 2016 at 13:51 UTC
    An alternative approach is to pack and unpack the array from the Perl side:
    # from perl sub do_nothing { my $doubles = pack 'd*', @_; _do_nothing_c($doubles); return unpack 'd*', $doubles; } # from XS void _do_nothing_c(sv) SV *sv PREINIT: STRLEN len; char *pv; CODE: pv = SvPV_force(sv, len); do_nothing((double *)pv, len / sizeof(double));
      An alternative approach is to pack and unpack the array from the Perl side

      Cool approach - it enables one to utilise the do_nothing() sub as it was presented in the original post.
      For my own benefit, I turned it into an Inline::C demo:
      use strict; use warnings; use Inline C => Config => BUILD_NOISY => 1, USING => 'ParseRegExp', ; use Inline C => <<'EOC'; double do_nothing(double x[], int sizeOfX) { int index; for(index=0; index<sizeOfX; index++) x[index] = 1; return x[0]; } void do_nothing_c(SV * sv) { STRLEN len; char * pv; pv = SvPV_force(sv, len); /* SvPV() also seems ok */ do_nothing((double *)pv, len / sizeof(double)); } EOC my @in = (2.3, 3.4, 4.5, 5.6, 6.7); my @ret = do_nothing(@in); print "@in\n@ret\n"; sub do_nothing { my $doubles = pack 'd*', @_; do_nothing_c($doubles); return unpack 'd*', $doubles; } __END__ Outputs: 2.3 3.4 4.5 5.6 6.7 1 1 1 1 1
      I was initially a little concerned about there being both a Csub and a Perlsub named "do_nothing" but, of course, the Csub does not bind to Inline::C and the only sub named "do_nothing" that is visible from perl space is the Perlsub. Conversely, the only sub named "do_nothing" that is visible from XS space is the Csub.

      The return value of the do_nothing() Csub is not being captured anywhere in this demo. For the purposes of this demo it might just as well have been coded as:
      void do_nothing(double x[], int sizeOfX) { int index; for(index=0; index<sizeOfX; index++) x[index] = 1; }

      Cheers,
      Rob