Perl Inline C: передача функции Arrayref в 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.

Любые идеи, что я делаю неправильно? Помогите с благодарностью!

Ответ 1

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

Массив - это экземпляр C struct, называемый AV. Ссылка выполняется с помощью struct, называемой RV. Все это "подтипы" (вид) базы struct, называемые SV.

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

  • Измените тип параметра на SV * (указатель на SV).
  • Используйте API для проверки того, является ли этот конкретный SV ссылкой, а не другим скалярным
  • Проверьте RV, чтобы убедиться, что он указывает на массив, а не что-то еще.
  • Разберите RV, чтобы получить SV, на который он указывает.
  • Так как мы знаем, что SV - это массив, добавьте его в AV и начните работать с ним.
  • Посмотрите третий элемент этого массива, который является еще одним SV.
  • Убедитесь, что SV, который мы получили из массива, является числовым значением, подходящим для C printf
  • Извлеките фактическое числовое значение из SV.
  • Распечатайте сообщение

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

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

Kinda заставляет вас оценить, сколько работы Perl делает под капотом.:)

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

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

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

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

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

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

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

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

Ответ 2

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

use Inline 'C';

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

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

Ответ 3

В Inline:: C код:

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

  array = (AV *)SvRV( reference );

  ...
}

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