Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Arrays and Inline C

by BobQQ (Initiate)
on Aug 01, 2008 at 16:14 UTC ( [id://701736]=perlquestion: print w/replies, xml ) Need Help??

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

Hi everybody, I have a script that does rather a lot of number crunching, so I thought I might be able to speed it up by using the Inline::C module. Unfortunately, I can't figure out how pass a couple of arrays to my C subroutine, or how to get a new one out (all the arrays are of known size). Can anyone suggest a simple way to do this? Thanks in advance

Replies are listed 'Best First'.
Re: Arrays and Inline C
by zentara (Archbishop) on Aug 01, 2008 at 16:42 UTC
    I'm not an Inline expert, but here are some examples I have from previous experimenting. The thing you need to consider is the type of data in the arrays, Perl takes care of it for you automatically, but in C, you need to be exact.

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: Arrays and Inline C (pack)
by tye (Sage) on Aug 02, 2008 at 02:01 UTC

    For some reason, people tend to think of accessing Perl arrays from C code. I think you'll run into a lot fewer bugs if you instead deal with C arrays from Perl code, since the code is so trivial to write:

    use Inline ( 'C', <<END_C ); void _munge( char *outlist, int size, char *inlist1, char *inlist2 ) { double *output = (double *) outlist; double *list1 = (double *) inlist1; double *list2 = (double *) inlist2; /* use the "arrays" just like normal for C code: */ int i; for( i= 0; i < size; ++i ) { output[i] = combine( list1[i], list2[i] ); } } END_C sub munge { my( $l1, $l2 ) = @_; my $size = 0+@$l1; my $out = "\0" x ( $size * length pack "d", 0.0 ); my $pack1 = pack "d$size", @$l1; my $pack2 = pack "d$size", @$l2; _munge( $out, $size, $pack1, $pack2 ); return unpack "d$size", $out; }

    - tye        

      Thanks for the example - but somehow the trick doesn't seem to work:
      use Inline 'C', <<'END_C' void _munge( char *outlist, int size, char *inlist1, char *inlist2 ) { double *output = (double *) outlist; double *list1 = (double *) inlist1; double *list2 = (double *) inlist2; int i; for( i= 0; i < size; ++i ) { printf("%f %f\n", inlist1[i], inlist2[i]); } } END_C ; my $size = 3; my $out = "\0" x ( $size * length pack "d", 0.0 ); my $pack1 = pack "d$size", 1, 2, 3; my $pack2 = pack "d$size", 4, 5, 6; _munge( $out, $size, $pack1, $pack2 );
      This prints
      0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
      I expected it to print the numbers passed in. What happened?

        You are passing 'char' values to your printf(), not 'double' values. Here is tested code that also includes the returning of values back to Perl:

        #!/usr/bin/perl -w use strict; use Inline 'C', <<'END_C'; void _munge( char *outbuf, int size, char *inbuf1, char *inbuf2 ) { double *output = (double *) outbuf; double *list1 = (double *) inbuf1; double *list2 = (double *) inbuf2; int i; for( i= 0; i < size; ++i ) { printf( "%f %f\n", list1[i], list2[i] ); output[i]= list1[i] * list2[i]; } } END_C my $size = 3; my $out = "\0" x ( $size * length pack "d", 0.0 ); my $pack1= pack "d$size", 1, 2, 3; my $pack2= pack "d$size", 4, 5, 6; _munge( $out, $size, $pack1, $pack2 ); print $_, $/ for unpack "d$size", $out;
        $ perl double.pl 1.000000 4.000000 2.000000 5.000000 3.000000 6.000000 4 10 18

        - tye        

Re: Arrays and Inline C
by spivey49 (Monk) on Aug 01, 2008 at 16:39 UTC

    Have a look at the Cookbook

    Basically you pass and return the variable much like you would call any other function

    use Inline C; $var = "something"; somefunc($var); __END__ __C__ somefunc(char* str){ . . return something; }

Re: Arrays and Inline C
by tod222 (Pilgrim) on Aug 01, 2008 at 22:14 UTC
Re: Arrays and Inline C
by syphilis (Archbishop) on Aug 02, 2008 at 01:31 UTC
    zentara provided an example of pass by reference. You can also pass by array - but, as with perl, C sees the argument list as one big array, not 2 separate arrays. So you need to supply the array sizes as arguments.

    In the demo below, both arrays are the same size, so it's only necessary to supply one "size" argument.
    use warnings; use Devel::Peek; use Inline C => <<'EOC'; AV * foo(SV * x, ...) { Inline_Stack_Vars; long array_size, i; AV * ret = newAV(); array_size = SvUV(Inline_Stack_Item(0)); /* check that array_size matches our expectations, based on the total number of arguments supplied */ if(Inline_Stack_Items != (2 * array_size) + 1) croak("Mismatch in number of arguments supplied to foo()"); /* for efficiency, make the array big enough in one fell swoop */ av_extend(ret, array_size - 1); /* multiply corresponding array elements and push the result into ret +*/ for(i = 1; i <= array_size; i++) av_push(ret, newSVnv(SvNV(Inline_Stack_Item(i)) * SvNV(Inline_Stack_Item(i + array_size))) +); return ret; } EOC my @num1 = (2.2, 3.3, 4.4,5.5); my @num2 = (6.6, 7.7, 8.8, 9.9); $arref = foo(scalar(@num1), @num1, @num2); print "@$arref\n"; # prints 14.52 25.41 38.72 54.45
    If the values in the arrays are unsigned integers, replace SvNV with SvUV, and replace newSVnv with newSVuv.
    If the values are signed integers, replace SvNV with SvIV, and replace newSVnv with newSViv.

    Cheers,
    Rob

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://701736]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (4)
As of 2024-04-19 22:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found