Perl Inline C: передача массива в функцию C

Я не могу передать arrayrefs в функцию C с помощью Inline C. Мне нужна помощь, пожалуйста.

Во-первых, просто чтобы доказать, что я могу заставить Inline C работать, я передам скалярное значение функции C:

#!/usr/bin/perl -I. 
#
# try1.pl
#
use Inline C;

my $c = 3.8;
foo( $c );

__END__
__C__
void foo( double c )
{
   printf( "C = %f\n", c );
}

И запустите это:

% ./try1.pl
C = 3.800000

Теперь сделайте то же самое, но с помощью arrayref:

#!/usr/bin/perl -I. 
# 
# try2.pl
#
use Inline C;

my @abc = (1.9, 2.3, 3.8);
foo( \@abc );

__END__
__C__
void foo( double *abc )
{
    printf( "C = %f\n", abc[2] );
}

Запустить его:

% ./try2.pl
Undefined subroutine &main::foo called at ./try1.pl line 7.

Есть идеи, что я делаю не так? Помощь высоко ценится!

3 ответа

Inline::C достаточно умен, чтобы извлечь значения из SV основано на сигнатуре типа вашей функции C Но если вы хотите передать сложные структуры Perl в функции C, вам нужно использовать Perl API для извлечения значений. Итак, вот что вам нужно знать для этой проблемы:

Массив является экземпляром C struct называется AV, Ссылка реализуется struct называется RV, Все это "подтипы" (своего рода) базы struct называется SV,

Поэтому, чтобы эта функция работала, нам нужно сделать несколько вещей.

  1. Измените тип параметра на SV * (указатель на SV).
  2. Используйте API, чтобы проверить, является ли этот конкретный SV является ссылкой в ​​отличие от некоторого другого вида скаляров
  3. Проверьте RV, чтобы убедиться, что он указывает на массив, а не на что-то другое.
  4. Разыменовывать RV чтобы получить SV на что это указывает.
  5. Поскольку мы знаем, что SV это массив, приведите его к AV и начать работать с ним.
  6. Поиск третий элемент этого массива, который является другим SV,
  7. Проверьте, что SV мы получили из массива числовое значение, подходящее для C printf
  8. Извлеките фактическое числовое значение из SV,
  9. Распечатать сообщение

Итак, собрав все это вместе, мы получим что-то вроде этого:

use Inline C;

my @abc = (1.9, 2.3, 3.8);
foo( \@abc );

__END__
__C__
void foo( SV *abc )
{
    AV *array;     /* this will hold our actual array */
    SV **value;    /* this will hold the value we extract, note that it is a double pointer */
    double num;    /* the actual underlying number in the SV */

    if ( !SvROK( abc ) ) croak( "param is not a reference" );
    if ( SvTYPE( SvRV( abc ) ) != SVt_PVAV ) croak( "param is not an array reference" );

    /* if we got this far, then we have an array ref */
    /* now dereference it to get the AV */
    array = (AV *)SvRV( abc );

    /* look up the 3rd element, which is yet another SV */
    value = av_fetch( array, 2, 0 );

    if ( value == NULL ) croak( "Failed array lookup" );
    if ( !SvNOK( *value ) ) croak( "Array element is not a number" );

    /* extract the actual number from the SV */
    num = SvNV( *value );

    printf( "C = %f\n", num );
}

В некотором роде вы понимаете, как много работы Perl выполняет под капотом.:)

Теперь вам не нужно быть настолько явным, как этот пример. Вы можете избавиться от некоторых временных переменных, выполнив что-то встроенное, например,

printf( "C = %f\n", SvNV( *value ) );

устранит необходимость объявлять num, Но я хотел прояснить, сколько разыменования и проверки типов необходимо для обхода структуры Perl в C.

И как @mob указывает ниже, вам на самом деле не нужно делать всю эту работу (хотя это хорошая идея, чтобы ознакомиться с тем, как она работает.)

Inline::C достаточно умен, чтобы, если вы объявите свою функцию как

void foo( AV *abc ) { 
   ...
}

Это автоматически развернет AV для вас, и вы можете пойти прямо к av_fetch шаг.

Если вам все это кажется непонятным, я настоятельно рекомендую взглянуть на:

  1. Perlguts Иллюстрированный PDF, затем
  2. perlguts manpage, а затем
  3. The Inline::C Cookbook, консультируя
  4. perlapi страница руководства.

Неверный тип данных.

use Inline 'C';

my @abc = (1.9, 2.3, 3.8);
foo( \@abc );

__END__
__C__
void foo(SV* abc) {
    sv_dump(abc);
}

В вашем коде Inline::C:

void foo( SV *reference ) {
  AV *array;

  array = (AV *)SvRV( reference );

  ...
}

Затем обработайте значение массива как AV тип. Смотрите Perl Inline::C Cookbook.

Другие вопросы по тегам