Search code examples
perlswigc-api

SWIG typedef - Perl array


I have a SWIG file to make many bindings for languages. There is a variable in C source, which is a fixed length list, with integer type. When I'm accessing to that in Perl, that has no items - just shows it's an ARRAY.

Same problem was in Python, but I could fixed that with a SWIG typemap:

#ifdef SWIGPYTHON
%typemap(out) int [ANY] {
   ...

Ok, I'ld like to do it again with Perl - but I'm not a Perl expert, so I can fix this typemap for Perl. This is what I try:

#ifdef SWIGPERL
%typemap(out) int [ANY] {
    SV* sv = newSV(0);
    AV* av = (AV *)sv_2mortal((SV *)newAV());
    int i = 0,len = 0;
    printf("len: %d\n", $1_dim0);
    len = $1_dim0;

    for (i = 0; i < len ; i++) {
        SV* perlval = newSV(0);
        sv_setiv(perlval, (IV)$1[i]);
        av_push(av, perlval);
    }
    SvSetSV($result, newRV_noinc((SV *)av));
}
#endif

The perl script shows me the "len: 10", when it runs, but the array is empty:

$i=1;
foreach(@m) {
    print $i, "'", $_, "'\n";
    $i=$i+1;
}

the result is:

0''

What em'I missing?

Thanks,

a.

EDIT: here's the Perl script:

#!/usr/bin/perl

use example1;

# this function set up the built-in list in example1 module
example1::get_att(7);
my @m = $example1::m->{ilist};

# "m" is a C structure in C source, and it has an "int ilist[10];" member.
# get_att(int n) will fill this list with "n" number
# This will be accesable in binded lanugage (eg in Python) as like this:
# print m.ilist  -> [0, 3, 6, 9, 12, 15, 18, 0, 0, 0]
$i = 0;
foreach(@m) {
    print $i, "'", $_, "'\n";
    $i=$i+1;
}

Now, this script produces this result:

len: 10
0''

The "len: 10" output came from SWIG typedef - see "printf("len: %d..." line...


Solution

  • I have not used SWIG before, but I thought it could be intersting to learn a little bit about it. After reading the documentation for hours, I think I might have found something that could solve your problem. First we need the interface file example.i:

    %module example
    
    %typemap(out) int [ANY] {
        AV* av = newAV();
        int i = 0,len = 0;
        len = $1_dim0;
    
        for (i = 0; i < len ; i++) {
            SV* perlval = newSV(0);
            sv_setiv(perlval, (IV)$1[i]);
            av_push(av, perlval);
        }
        $result = newRV_noinc((SV*) av );
        sv_2mortal( $result );
        argvi++;
    }
    
    %typemap(in) int [ANY] {
      AV *tempav;
      I32 len;
      int i;
      SV  **tv;
      if (!SvROK($input))
        croak("Argument $argnum is not a reference.");
      if (SvTYPE(SvRV($input)) != SVt_PVAV)
        croak("Argument $argnum is not an array.");
      tempav = (AV*)SvRV($input);
      len = av_len(tempav);
      $1 = (int *) malloc((len+1)*sizeof(int));
      for (i = 0; i <= len; i++) {
        tv = av_fetch(tempav, i, 0);
        $1[i] = (int) SvIV(*tv);
      }
    }
    
    %typemap(freearg) int * {
      free($1);
    }
    
    %typemap(memberin) int [ANY] {
      int i;
      for (i = 0; i < $1_dim0; i++) {
          $1[i] = $input[i];
      }
    }
    
    %inline %{
    
    struct m {
        int ilist[3];
    };
    
    %}
    

    Then we need to create a Perl script to test the example::m package:

    use feature qw(say);
    use strict;
    use warnings;
    
    use example;
    
    my $m = example::m->new();
    $m->{ilist} = [1, 2, 3];
    my $list = $m->{ilist};
    
    my $i = 0;
    for ( @$list ) {
        say "$i: '$_'";
        $i=$i+1;
    }
    

    The output is now (after compiling the interface file and running the Perl script):

    0: '1'
    1: '2'
    2: '3'
    

    Note:

    • I first installed SWIG 3.0.8 ( on Ubuntu 16.04 ) using

      $ sudo apt-get install swig
      
    • Then I compiled the interface file example.i using:

      $ swig -perl5 example.i
      $ gcc -fpic -c -Dbool=char -D_GNU_SOURCE \ 
         -I/usr/lib/x86_64-linux-gnu/perl/5.22/CORE example_wrap.c
      $ gcc -shared example.o example_wrap.o -o example.so