SWIG typedef - массив Perl

У меня есть файл SWIG, чтобы сделать много привязок для языков. В источнике C есть переменная, представляющая собой список фиксированной длины с целочисленным типом. Когда я обращаюсь к этому в Perl, у него нет элементов - просто показывает, что это массив.

Та же проблема была в Python, но я мог бы исправить это с помощью карты типов SWIG:

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

Хорошо, я хотел бы сделать это снова с Perl - но я не эксперт Perl, поэтому я могу исправить эту карту типов для Perl. Вот что я пытаюсь:

#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

Скрипт perl показывает мне "len: 10", когда он запускается, но массив пуст:

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

результат:

0 ''

Что мне не хватает?

Спасибо,

а.

РЕДАКТИРОВАТЬ: вот сценарий Perl:

#!/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;
}

Теперь этот скрипт выдает такой результат:

len: 10
0''

Вывод "len: 10" был получен из SWIG typedef - см. Строку "printf("len: %d..."...

1 ответ

Решение

Я не использовал SWIG раньше, но я подумал, что было бы интересно узнать об этом немного. Прочитав документацию в течение нескольких часов, думаю, я мог бы найти что-то, что могло бы решить вашу проблему. Сначала нам нужен файл интерфейса 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];
};

%}

Затем нам нужно создать Perl-скрипт для проверки example::m пакет:

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;
}

Вывод теперь (после компиляции файла интерфейса и запуска сценария Perl):

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

Примечание:

  • Сначала я установил SWIG 3.0.8 (в Ubuntu 16.04), используя

    $ sudo apt-get install swig
    
  • Затем я скомпилировал файл интерфейса example.i с помощью:

    $ 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
    
Другие вопросы по тегам