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
,
Поэтому, чтобы эта функция работала, нам нужно сделать несколько вещей.
- Измените тип параметра на
SV *
(указатель наSV
). - Используйте API, чтобы проверить, является ли этот конкретный
SV
является ссылкой в отличие от некоторого другого вида скаляров - Проверьте RV, чтобы убедиться, что он указывает на массив, а не на что-то другое.
- Разыменовывать
RV
чтобы получитьSV
на что это указывает. - Поскольку мы знаем, что
SV
это массив, приведите его кAV
и начать работать с ним. - Поиск третий элемент этого массива, который является другим
SV
, - Проверьте, что
SV
мы получили из массива числовое значение, подходящее для Cprintf
- Извлеките фактическое числовое значение из
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 );
}
В некотором роде вы понимаете, как много работы Perl выполняет под капотом.:)
Теперь вам не нужно быть настолько явным, как этот пример. Вы можете избавиться от некоторых временных переменных, выполнив что-то встроенное, например,
printf( "C = %f\n", SvNV( *value ) );
устранит необходимость объявлять num
, Но я хотел прояснить, сколько разыменования и проверки типов необходимо для обхода структуры Perl в C.
И как @mob указывает ниже, вам на самом деле не нужно делать всю эту работу (хотя это хорошая идея, чтобы ознакомиться с тем, как она работает.)
Inline::C достаточно умен, чтобы, если вы объявите свою функцию как
void foo( AV *abc ) {
...
}
Это автоматически развернет AV
для вас, и вы можете пойти прямо к av_fetch
шаг.
Если вам все это кажется непонятным, я настоятельно рекомендую взглянуть на:
- Perlguts Иллюстрированный PDF, затем
-
perlguts
manpage, а затем - The Inline::C Cookbook, консультируя
-
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.