Passing short int arrays between Perl and C 
Author Message
 Passing short int arrays between Perl and C

Hello all,

I have recently written a small C-library that I would now like to bring
into perl.  I am having a problem, however, when I attempt to pass
arrays of short int's back and forth between the C library call and
Perl.

The C function expects three arguments--two pointers to arrays
(C-arrays) of short ints (and it assumes that the calling function has
taken care of the space allocations) and a length argument (an int).
One array contains the input to the function while the other is used for
the output.  On the Perl side I use 'pack' to create a "string"
containing all the data.  I then make a copy of this string (which I
figure makes a nice template for the output array from C) and pass
pointers to the scalar strings to the C routine along with a length
argument (both arrays will always be the same size).  C happily does its
thing.  The problem is that when I get back to perl if the returned
array happened to contain a 0 within the array the output string in perl
ends at the embedded .  (I smell strlen from C somewhere here).  Instead
of me babbling more let me include some code snippits:



$output = $input;     #allocate space for output.
&mypackage::c-library-call($input, $length, $output);

(Eventually I'd like to hide the packing and space allocation in a perl
wrapper in mypackage).

C-LIBRARY CALL:
int c-library-call(short int input*, int length, short int *output)

XS CODE:
int
c-library-call(arg0, arg1, arg2)
        short * arg0
        int     arg1
        short * arg2
        OUTPUT:
        arg2

TYPEMAP:
short *                         T_PV

C-WRAPPER PRODUCED BY XS COMPILER:
XS(XS_c-library-call)
{
    dXSARGS;
    if (items != 3)
        croak("Usage: mypackage::c-library-call(arg0, arg1, arg2)");
    {
        short * arg0 = (short *)SvPV(ST(0),na);
        int     arg1 = (int)SvIV(ST(1));
        short * arg2 = (short *)SvPV(ST(2),na);
        int     RETVAL;

        RETVAL = c-library-call(arg0, arg1, arg2);
        sv_setpv((SV*)ST(2), arg2);
        ST(0) = sv_newmortal();
        sv_setiv(ST(0), (IV)RETVAL);
    }
    XSRETURN(1);

Quote:
}

I've tried a few variations on the XS file playing around with various
(writing some of my own code via CODE: and PPCODE:) but have had no luck
so far.

I really appreciate any help you folks can provide.

Thanks,
--Rob



Sat, 17 Jun 2000 03:00:00 GMT  
 Passing short int arrays between Perl and C

[...]
) The C function expects three arguments--two pointers to arrays
) (C-arrays) of short ints (and it assumes that the calling function has
) taken care of the space allocations) and a length argument (an int).
) [...]  The problem is that when I get back to perl if the returned
) array happened to contain a 0 within the array the output string in perl
) ends at the embedded.
[...]



) $output = $input;     #allocate space for output.
) &mypackage::c-library-call($input, $length, $output);
[...]
) XS(XS_c-library-call)
) {
)     dXSARGS;
)     if (items != 3)
)         croak("Usage: mypackage::c-library-call(arg0, arg1, arg2)");
)     {
)         short * arg0 = (short *)SvPV(ST(0),na);
)         int     arg1 = (int)SvIV(ST(1));
)         short * arg2 = (short *)SvPV(ST(2),na);
)         int     RETVAL;
)
)         RETVAL = c-library-call(arg0, arg1, arg2);
)         sv_setpv((SV*)ST(2), arg2);

The above line is the problem.  sv_setpv looks for a trailing '\0'.
The easiest fix based on the code you have here is to not list
"arg2" as an "OUTPUT" parameter:

) XS CODE:
) int
) c-library-call(arg0, arg1, arg2)
)         short * arg0
)         int     arg1
)         short * arg2
)         OUTPUT:
)         arg2

That is, drop the last two lines above.

But that requires that the Perl code that calls your XS
code to be very careful.  I'd advocate something like
the following:


and uses Perl's notion of the string's length to either 1)
determine the number of elements in the array [that is,
don't bother having the array length passed in as a separate
argument] or, more flexible, 2) verify that the array length
passed in is not too large for the input string.  Then XS code
makes sure the output parameter has sufficient buffer space
for matching output array and calls C code:

XS CODE:
int
c_library_call( asInput, iCount, asOutput )
        short * asInput
        int     iCount
        SV *    svOutput
    CODE:
        if(  iCount*sizeof(short) < SvCUR(ST(0))  ) {
            errno= EINVAL;      /* SETERRNO( EINVAL, -1 ); with my patch */
            RETVAL= -1;         /* Or whatever the error return is */
        } else {
          short *asOutput;
            /* [The following steps taken from Perl's sysread()] */
            /* If svOutput is undef, SvPV_force() isn't enough... */
            if(  ! SvOK(svOutput)  )   sv_setpn( svOutput, "", 0 );
            /* Force svOutput to have a string buffer allocated to it: */
            (void) SvPV_force( svOutput, na );
            /* Ensure that the string buffer is large enough: */
            asOutput= (short *) SvGROW( svOutput, iCount*sizeof(short)+1 );
            /* [the above "+1" is for the "safety" trailing '\0'] */
            RETVAL= c_library_call( asInput, iCount, asOutput );
            /* In case user accidentally passes string to some C function: */
            ((char *)asOutput)[iCount*sizeof(short)]= '\0';
            /* Let Perl know the length of the output buffer: */
            SvCUR_set( svOutput, iCount*sizeof(short) );
        }
    OUTPUT:
        RETVAL

Unfortunately, I haven't found a way to get the "typemap" stuff
to handle these types of length-dependant initialization issues.
Note that the automatic connection between "ST(0)" and "asInput"
is lost in the above code and you have to be very careful to keep
the mapping correct if you change the arguments to the function.
--
Tye McQueen    Nothing is obvious unless you are overlooking something
         http://www.metronet.com/~tye/ (scripts, links, nothing fancy)
       Remove d's from address to reply (sorry for the inconvenience).



Sat, 17 Jun 2000 03:00:00 GMT  
 
 [ 2 post ] 

 Relevant Pages 

1. Looking for XS example: passing (int **)

2. Hot novel by CS professor, includes perl code

3. int() array count problem

4. Packed Int Array

5. Packed Int Array

6. short question - short answer

7. Comparing Arrays (short question)

8. Passing arrays of arrays in perlxs

9. Passing arrays of arrays in perlxs

10. Passing arrays, scalars, and assoc. arrays

11. Australian FTP site for perl4.0 - escher.gallery.cs.unsw.oz.au

12. Soliciting views on PLs in CS education

 

 
Powered by phpBB® Forum Software