2009年12月18日星期五

perlxstut: 从perl给c传递数组参数

Click here to download:
Array-0.01.tar.gz (44 KB)

=head1 NAME

    如何从perl中将数组传入xs

=head1 Prework
   
    h2xs -A -n Array


=head2 从perl给c传递数组

    typedef int intArray;
    int sum(int count, intArray * array); //c原型

    intArray * T_ARRAY  // typemap
   
    int sum_as_list(array, ...)
        intArray * array
      CODE:
        RETVAL = sum(ix_array, array);
      OUTPUT:
        RETVAL
      CLEANUP:
        Safefree(array)

从perl中传递数组给c需要经过perl预定义的T_ARRAY的处理, T_ARRAY定义在perl的标准typemap中。T_ARRAY会生成一个ix_${var}的变量来表示传入参数的个数,比如在上面的例子中,生成ix_array。$var为传入参数和传到c中的参数名

在c中当然需要有一个保存传入参数的数组,T_ARRAY假设存在一个叫做$ntype(这里为intAarryPtr)的函数,这个函数返回一个指向内存的指针,T_ARRAY使用这个指针来保存参数列表,自然的,这个指针需要在c函数返回后被释放,不然便会造成内存泄漏。这就是上面例子中CLEANUP的作用。当然我们也可以自己定义,在$ntype中添加释放内存的操作。比如

!!!! 改正,perl不会给我们自动提供实现提供内存的函数,这些还得我们自己定义.
  
    //这是默认实现
    intArray * intArrayPtr(int num){
        intArray * array;
        New(0,array, num, intArray);
        return array
    }

    //无须cleanup的实现版本
    void * intArrayPtr(int num){
        SV * mortal;
        mortal = sv_2mortal(NEWSV(0,num * sizeof(intArray)));
        return SvPVX(mortal);
    }
   

c里面是如何决定array的类型的?答案是从$ntype中猜测,perl把ntype中的array和ptr字段去除,剩下的字段就是数组的类型。在这里,把intArrayPtr去掉便成了int

标准的T_ARRAY typemap可以在/usr/lib/perl5/5.10/ExtUtils/typemap中找到


=head2 给c传递数组引用

    int
    sum_as_ref(avref)
            AV * avref
        PREINIT:
            int len;
            int i;
            SV ** elem;
            intArray * array;
        CODE:
            len = av_len(avref) + 1;
            array = intArrayPtr(len);
            for(i = 0; i<len; i++){
                elem = av_fetch(avref, i, 0);
                if(elem == NULL){
                    array[i] = 0;
                }
                else{
                    array[i] = SvIV(*elem);
                }
            }
            RETVAL = sum(len, array);
        OUTPUT:
            RETVAL

在这里av_fetch返回的是一个指向sv的引用的引用,其第三个参数是bool lvalue,当这个参数被设置时,如果数组不够大,perl会自动根据第二个参数index扩充数组。


=head2 使用pack给c传递参数

    int
    sum_as_packed(packed)
            SV * packed
        PREINIT:
            int len;
            intArray * array;
        CODE:
            array = (intArray *) SvPV_nolen(packed);
            len = SvCUR(packed)/sizeof(intArray);
            RETVAL = sum(len, array);
        OUTPUT:
            RETVAL
   
    int
    sum_as_packed2(len, packed)
            int len
            char * packed
        CODE:
            RETVAL = sum(len,(intArray *)packed);
        OUTPUT:
            RETVAL

SvPV无非是从SV返回一个PV,而SvCUR返回的是Sv的大小。

=cut

Posted via email from 单行道

没有评论: