perlxstut
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- СПЕЦИАЛЬНЫЕ ПРИМЕЧАНИЯ
- УРОК
- ПРИМЕР 1
- ПРИМЕР 2
- Что произошло?
- Написание хороших тестовых скриптов
- ПРИМЕР 3
- Что нового?
- Параметры ввода и вывода
- Программа XSUBPP
- Файл TYPEMAP
- Предупреждение об аргументах вывода
- ПРИМЕР 4
- Что произошло здесь?
- Структура файла .xs
- Удаление лишнего из XSUB
- Подробнее об аргументах XSUB
- Стек аргументов
- Расширение вашего расширения
- Документирование вашего расширения
- Установка вашего расширения
- ПРИМЕР 5
- Новые элементы в этом примере
- ПРИМЕР 6
- Новые элементы в этом примере
- ПРИМЕР 7 (В разработке)
- ПРИМЕР 8 (В разработке)
- ПРИМЕР 9: Передача открытых файлов в XSes
- Отладка этих примеров
- См. также
- Автор
ИМЯ
perlxstut - Руководство по написанию XSUB
ОПИСАНИЕ
Этот учебник познакомит читателя с этапами создания расширения Perl. Предполагается, что читатель имеет доступ к perlguts, perlapi и perlxs.
Этот учебник начинается с очень простых примеров и становится более сложным, при этом каждый новый пример добавляет новые функции. Некоторые концепции могут быть полностью объяснены только в более поздних разделах учебника, чтобы постепенно подготовить читателя к созданию расширений.
Этот учебник написан с точки зрения Unix. Там, где я знаю о различиях на других платформах (например, Win32), я их укажу. Если вы найдете что-то пропущенное, сообщите мне, пожалуйста.
СПЕЦИАЛЬНЫЕ ПРИМЕЧАНИЯ
make
Этот учебник предполагает, что программа make, используемая Perl, называется make. В примерах, которые следуют, вместо запуска "make" вам может потребоваться заменить его на программу make, с которой сконфигурирован Perl. Вы можете узнать её, выполнив команду perl -V:make.
Ограничения версии
При написании расширения Perl для общего использования следует ожидать, что оно будет использоваться с версиями Perl, отличными от версии на вашей машине. Поскольку вы читаете этот документ, версия Perl на вашей машине, вероятно, 5.005 или новее, но пользователи вашего расширения могут иметь более старые версии.
Чтобы понять, какие несовместимости могут возникнуть, и в редких случаях, если версия Perl на вашей машине старше, чем этот документ, см. раздел «Отладка этих примеров» для получения дополнительной информации.
Если ваше расширение использует какие-либо функции Perl, недоступные в более старых выпусках Perl, ваши пользователи оценят раннее и осмысленное предупреждение. Вы, вероятно, поместите эту информацию в файл README, но в настоящее время установка расширений может выполняться автоматически, управляемая модулем CPAN.pm или другими инструментами.
В установках, основанных на MakeMaker, Makefile.PL предоставляет первую возможность для проверки версий. Вы можете поместить что-то вроде этого в Makefile.PL для этой цели:
eval { require 5.007 }
or die <<EOD;
############
### This module uses frobnication framework which is not available
### before version 5.007 of Perl. Upgrade your Perl before
### installing Kara::Mba.
############
EOD Динамическая загрузка против статической загрузки
Общепринято считать, что если система не имеет возможности динамической загрузки библиотеки, вы не можете создавать XSUB. Это неверно. Вы можете их создать, но вам необходимо связать подпрограммы XSUB со всем остальным Perl, создав новый исполняемый файл. Эта ситуация аналогична Perl 4.
Этот учебник все равно можно использовать на такой системе. Механизм построения XSUB проверит систему и создаст динамически загружаемую библиотеку, если это возможно, или же статическую библиотеку, а затем, необязательно, новый статически связанный исполняемый файл с этой статической библиотекой, связанной в нем.
Если вы хотите создать статически связанный исполняемый файл на системе, которая может динамически загружать библиотеки, вы можете во всех следующих примерах, где выполняется команда "make" без аргументов, выполнить команду "make perl" вместо неё.
Если вы создали такой статически связанный исполняемый файл по своему желанию, то вместо того, чтобы писать "make test", вы должны написать "make test_static". На системах, которые вообще не могут создавать динамически загружаемые библиотеки, достаточно написать "make test".
Потоки и PERL_NO_GET_CONTEXT
Для многопоточных сборок Perl требуется указатель контекста для текущего потока, без PERL_NO_GET_CONTEXT, Perl будет вызывать функцию для получения контекста.
Для повышения производительности включите:
#define PERL_NO_GET_CONTEXT как показано ниже.
Для получения более подробной информации см. perlguts.
УРОК
Теперь перейдем к делу!
ПРИМЕР 1
Наше первое расширение будет очень простым. Когда мы вызываем процедуру в расширении, она выведет известное сообщение и вернет значение.
Запустите "h2xs -A -n Mytest". Это создаст каталог Mytest, возможно, в ext/, если этот каталог существует в текущей рабочей директории. Под каталогом Mytest будут созданы несколько файлов, включая MANIFEST, Makefile.PL, lib/Mytest.pm, Mytest.xs, t/Mytest.t и Changes.
Файл MANIFEST содержит имена всех созданных файлов в каталоге Mytest.
Файл Makefile.PL должен выглядеть примерно так:
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'Mytest',
VERSION_FROM => 'Mytest.pm', # finds $VERSION
LIBS => [''], # e.g., '-lm'
DEFINE => '', # e.g., '-DHAVE_SOMETHING'
INC => '-I', # e.g., '-I. -I/usr/include/other'
); Файл Mytest.pm должен начинаться примерно так:
package Mytest;
use 5.008008;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw(
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
);
our $VERSION = '0.01';
require XSLoader;
XSLoader::load('Mytest', $VERSION);
# Preloaded methods go here.
1;
__END__
# Below is the stub of documentation for your module. You better
# edit it! Остальная часть файла .pm содержит примеры кода для предоставления документации для расширения.
Наконец, файл Mytest.xs должен выглядеть примерно так:
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
MODULE = Mytest PACKAGE = Mytest Давайте изменим файл .xs, добавив это в конец файла:
void
hello()
CODE:
printf("Hello, world!\n"); Строки, начинающиеся с "CODE:", можно не отступать. Однако для повышения читабельности рекомендуется отступать строку CODE: на один уровень, а следующие строки - ещё на один уровень.
Теперь запустим "perl Makefile.PL". Это создаст настоящий Makefile, который нужен make. Его вывод выглядит примерно так:
% perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for Mytest
% Теперь, выполнив команду make, вы получите вывод, похожий на этот (некоторые длинные строки были укорочены для ясности, а некоторые лишние строки были удалены):
% make
cp lib/Mytest.pm blib/lib/Mytest.pm
perl xsubpp -typemap typemap Mytest.xs > Mytest.xsc && \
mv Mytest.xsc Mytest.c
Please specify prototyping behavior for Mytest.xs (see perlxs manual)
cc -c Mytest.c
Running Mkbootstrap for Mytest ()
chmod 644 Mytest.bs
rm -f blib/arch/auto/Mytest/Mytest.so
cc -shared -L/usr/local/lib Mytest.o -o blib/arch/auto/Mytest/Mytest.so
chmod 755 blib/arch/auto/Mytest/Mytest.so
cp Mytest.bs blib/arch/auto/Mytest/Mytest.bs
chmod 644 blib/arch/auto/Mytest/Mytest.bs
Manifying blib/man3/Mytest.3pm
% Вы можете спокойно пропустить строку о «поведении прототипирования» - она объяснена в "The PROTOTYPES: Keyword" в perlxs.
У Perl есть свой собственный особый способ написания тестовых скриптов, но для этого примера мы создадим свой собственный тестовый скрипт. Создайте файл hello, который выглядит так:
#! /opt/perl5/bin/perl
use ExtUtils::testlib;
use Mytest;
Mytest::hello(); Теперь сделайте скрипт исполняемым (chmod +x hello), запустите скрипт, и мы должны увидеть следующий вывод:
% ./hello
Hello, world!
% ПРИМЕР 2
Теперь добавим в наше расширение подпрограмму, которая будет принимать один числовой аргумент в качестве входных данных и возвращать 1, если число чётное, или 0, если нечётное.
Добавьте следующее в конец Mytest.xs:
int
is_even(input)
int input
CODE:
RETVAL = (input % 2 == 0);
OUTPUT:
RETVAL Пробелы в начале строки "int input" не обязательны, но они полезны для повышения читабельности. Размещение точки с запятой в конце этой строки также необязательно. Любое количество и вид пробелов может быть размещено между "int" и "input".
Теперь перезапустите make для перестроения нашей новой динамической библиотеки.
Теперь выполните те же шаги, что и раньше, сгенерируйте Makefile из Makefile.PL и запустите make.
Для проверки работы нашего расширения, нам теперь нужно посмотреть на файл Mytest.t. Этот файл настроен для имитации той же структуры тестирования, которая есть у самого Perl. Внутри тестового скрипта вы выполняете ряд тестов для подтверждения поведения расширения, печатая "ok" при корректном тесте и "not ok" при ошибке.
use Test::More tests => 4;
BEGIN { use_ok('Mytest') };
#########################
# Insert your test code below, the Test::More module is use()ed here
# so read its man page ( perldoc Test::More ) for help writing this
# test script.
is( Mytest::is_even(0), 1 );
is( Mytest::is_even(1), 0 );
is( Mytest::is_even(2), 1 ); Мы будем вызывать тестовый скрипт с помощью команды "make test". Вы должны увидеть вывод, похожий на этот:
%make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e"
"test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/Mytest....ok
All tests successful.
Files=1, Tests=4, 0 wallclock secs ( 0.03 cusr + 0.00 csys = 0.03 CPU)
% Что произошло?
Программа h2xs является отправной точкой для создания расширений. В последующих примерах мы увидим, как можно использовать h2xs для чтения заголовочных файлов и генерации шаблонов для подключения к C-функциям.
h2xs создает ряд файлов в каталоге расширения. Файл Makefile.PL - это скрипт Perl, который сгенерирует настоящий Makefile для построения расширения. Мы рассмотрим его позже.
Файлы .pm и .xs содержат суть расширения. Файл .xs содержит C-функции, составляющие расширение. Файл .pm содержит процедуры, которые сообщают Perl, как загрузить ваше расширение.
Генерация Makefile и запуск make создали в текущем каталоге директорию blib (что означает "библиотека построения"). В этой директории будет находиться разделяемая библиотека, которую мы будем строить. После тестирования мы сможем установить её в конечное местоположение.
Вызов скрипта теста через "make test" выполнил очень важную операцию. Он вызвал perl со всеми этими -I аргументами, чтобы он мог найти различные файлы, являющиеся частью расширения. Очень важно, что при тестировании расширений вы используете "make test". Если вы попытаетесь запустить скрипт теста самостоятельно, вы получите ошибку fatal. Ещё одна причина, по которой важно использовать "make test" для запуска вашего скрипта теста, заключается в том, что если вы тестируете обновление уже существующей версии, использование "make test" гарантирует, что вы будете тестировать ваше новое расширение, а не уже существующую версию.
Когда Perl видит use extension;, он ищет файл с тем же именем, что и use расширение с расширением .pm. Если этот файл не найден, Perl завершается с ошибкой fatal. Путь поиска по умолчанию содержится в массиве @INC.
В нашем случае, Mytest.pm сообщает perl, что ему потребуются расширения Exporter и Dynamic Loader. Затем он устанавливает массивы @ISA и @EXPORT и скаляр $VERSION; и, наконец, сообщает perl о загрузке модуля. Perl вызовет свою процедуру динамической загрузки (если она есть) и загрузит разделяемую библиотеку.
Два массива @ISA и @EXPORT очень важны. Массив @ISA содержит список других пакетов, в которых следует искать методы (или подпрограммы), которые не существуют в текущем пакете. Это обычно важно только для расширений, ориентированных на объекты (о которых мы поговорим позже), поэтому обычно не требует модификаций.
Массив @EXPORT сообщает Perl, какие переменные и подпрограммы расширения должны быть помещены в пространство имён вызывающего пакета. Поскольку вы не знаете, уже использовал ли пользователь ваши имена переменных и подпрограмм, крайне важно тщательно выбрать, что экспортировать. Не экспортируйте имена методов или переменных по умолчанию без веской причины.
В качестве общего правила, если модуль ориентирован на объекты, то ничего не экспортируйте. Если это просто набор функций и переменных, то их можно экспортировать через другой массив, называемый @EXPORT_OK. Этот массив не автоматически помещает свои имена подпрограмм и переменных в пространство имён, если пользователь этого специально не запросит.
См. perlmod для получения дополнительной информации.
Переменная $VERSION используется для обеспечения "синхронизации" файла .pm и разделяемой библиотеки. Всякий раз, когда вы вносите изменения в файлы .pm или .xs, вы должны увеличивать значение этой переменной.
Написание хороших скриптов тестов
Важность написания хороших скриптов тестов трудно переоценить. Вы должны строго следовать стилю "ok/not ok", используемому самим Perl, чтобы было легко и однозначно определить результат каждого тестового случая. Когда вы находите и исправляет ошибку, убедитесь, что добавили тестовый случай для неё.
Выполняя "make test", вы гарантируете, что ваш скрипт Mytest.t работает и использует правильную версию вашего расширения. Если у вас много тестовых случаев, сохраните ваши тестовые файлы в каталоге "t" и используйте расширение ".t". Когда вы запускаете "make test", все эти тестовые файлы будут выполнены.
ПРИМЕР 3
Наше третье расширение примет одно значение в качестве входных данных, округлит его и установит аргумент к округленному значению.
Добавьте следующее в конец Mytest.xs:
void
round(arg)
double arg
CODE:
if (arg > 0.0) {
arg = floor(arg + 0.5);
} else if (arg < 0.0) {
arg = ceil(arg - 0.5);
} else {
arg = 0.0;
}
OUTPUT:
arg Отредактируйте файл Makefile.PL, чтобы соответствующая строка выглядела так:
LIBS => ['-lm'], # e.g., '-lm' Сгенерируйте Makefile и выполните make. Измените номер теста в Mytest.t на "9" и добавьте следующие тесты:
my $i;
$i = -1.5;
Mytest::round($i);
is( $i, -2.0, 'Rounding -1.5 to -2.0' );
$i = -1.1;
Mytest::round($i);
is( $i, -1.0, 'Rounding -1.1 to -1.0' );
$i = 0.0;
Mytest::round($i);
is( $i, 0.0, 'Rounding 0.0 to 0.0' );
$i = 0.5;
Mytest::round($i);
is( $i, 1.0, 'Rounding 0.5 to 1.0' );
$i = 1.2;
Mytest::round($i);
is( $i, 1.0, 'Rounding 1.2 to 1.0' ); Запуск "make test" теперь должен вывести, что все девять тестов прошли успешно.
Обратите внимание, что в этих новых тестовых случаях аргумент, переданный в round, был скалярной переменной. Возможно, вы задаётесь вопросом, можно ли округлить константу или литерал. Чтобы увидеть, что происходит, временно добавьте следующую строку в Mytest.t:
Mytest::round(3); Запустите "make test" и обратите внимание, что Perl завершается с ошибкой fatal. Perl не позволит вам изменить значение констант!
Что нового здесь?
-
Мы внесли некоторые изменения в Makefile.PL. В данном случае мы указали дополнительную библиотеку для подключения к разделяемой библиотеке расширения, в данном случае библиотеку математических функций libm. Позже мы обсудим, как писать XSUB, которые могут вызывать каждую функцию в библиотеке.
-
Значение функции не возвращается как значение функции, а изменяя значение переменной, переданной в функцию. Вы, возможно, догадались об этом, когда увидели, что возвращаемое значение round имеет тип "void".
Входные и выходные параметры
Вы указываете параметры, которые будут переданы в XSUB в строке(ах) после объявления возвращаемого значения и имени функции. Каждая строка входного параметра начинается с необязательных пробелов и может иметь необязательную заключительную точку с запятой.
Список выходных параметров находится в самом конце функции, сразу после директивы OUTPUT:. Использование RETVAL сообщает Perl, что вы хотите отправить это значение обратно как возвращаемое значение функции XSUB. В Примере 3 мы хотели, чтобы "возвращаемое значение" было размещено в исходной переменной, которую мы передали, поэтому мы указали её (а не RETVAL) в разделе OUTPUT:.
Программа XSUBPP
Программа xsubpp принимает код XS в файле .xs и преобразует его в код C, помещая его в файл с расширением .c. Созданный код C активно использует функции C внутри Perl.
Файл TYPEMAP
Программа xsubpp использует правила для преобразования типов данных Perl (скаляр, массив и т. д.) в типы данных C (int, char и т. д.). Эти правила хранятся в файле typemap ($PERLLIB/ExtUtils/typemap). Ниже приведено краткое обсуждение, но все подробности можно найти в perlxstypemap. Если у вас достаточно новая версия perl (5.16 и выше) или обновлённый компилятор XS (ExtUtils::ParseXS 3.13_01 или лучше), вы можете встраивать typemap в свой XS вместо создания отдельных файлов. В любом случае, этот typemap разделён на три части:
Первый раздел сопоставляет различные типы данных C с именем, которое в какой-то степени соответствует различным типам Perl. Второй раздел содержит код C, который xsubpp использует для обработки входных параметров. Третий раздел содержит код C, который xsubpp использует для обработки выходных параметров.
Давайте рассмотрим часть файла .c, созданного для нашего расширения. Имя файла — Mytest.c:
XS(XS_Mytest_round)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mytest::round(arg)");
PERL_UNUSED_VAR(cv); /* -W */
{
double arg = (double)SvNV(ST(0)); /* XXXXX */
if (arg > 0.0) {
arg = floor(arg + 0.5);
} else if (arg < 0.0) {
arg = ceil(arg - 0.5);
} else {
arg = 0.0;
}
sv_setnv(ST(0), (double)arg); /* XXXXX */
SvSETMAGIC(ST(0));
}
XSRETURN_EMPTY;
} Обратите внимание на две строки, помеченные "XXXXX". Если вы проверите первую часть файла typemap (или раздел), вы увидите, что числа с плавающей точкой имеют тип T_DOUBLE. В части INPUT typemap аргумент типа T_DOUBLE присваивается переменной arg путём вызова функции SvNV на чём-то, затем приведения её к double и последующего присваивания переменной arg. Аналогично, в разделе OUTPUT, после того, как arg получит своё конечное значение, оно передаётся функции sv_setnv для передачи вызывающей подпрограмме. Эти две функции объясняются в perlguts; мы подробнее поговорим о том, что значит "ST(0)" в разделе о стеке аргументов.
Предупреждение о выходных аргументах
В общем случае не рекомендуется создавать расширения, которые изменяют свои входные параметры, как в Примере 3. Вместо этого вы должны, вероятно, возвращать несколько значений в массиве и позволить вызывающей стороне обрабатывать их (мы сделаем это в последующем примере). Однако для лучшей поддержки вызова существующих C-процедур, которые часто изменяют свои входные параметры, такое поведение допускается.
ПРИМЕР 4
В этом примере мы начнём писать XSUB, которые будут взаимодействовать с предопределёнными C-библиотеками. Для начала мы создадим небольшую библиотеку, а затем позволим h2xs создать наши файлы .pm и .xs.
Создайте новый каталог Mytest2 на том же уровне, что и каталог Mytest. В каталоге Mytest2 создайте ещё один каталог mylib и перейдите в него.
Здесь мы создадим файлы, которые сгенерируют тестовую библиотеку. Эти файлы будут включать файл исходного кода C и заголовочный файл. Мы также создадим Makefile.PL в этом каталоге. Затем мы убедимся, что запуск make на уровне Mytest2 автоматически запустит этот файл Makefile.PL и созданный Makefile.
В каталоге mylib создайте файл mylib.h, который выглядит так:
#define TESTVAL 4
extern double foo(int, long, const char*); Также создайте файл mylib.c, который выглядит так:
#include <stdlib.h>
#include "mylib.h"
double
foo(int a, long b, const char *c)
{
return (a + b + atof(c) + TESTVAL);
} И, наконец, создайте файл Makefile.PL, который выглядит так:
use ExtUtils::MakeMaker;
$Verbose = 1;
WriteMakefile(
NAME => 'Mytest2::mylib',
SKIP => [qw(all static static_lib dynamic dynamic_lib)],
clean => {'FILES' => 'libmylib$(LIB_EXT)'},
);
sub MY::top_targets {
'
all :: static
pure_all :: static
static :: libmylib$(LIB_EXT)
libmylib$(LIB_EXT): $(O_FILES)
$(AR) cr libmylib$(LIB_EXT) $(O_FILES)
$(RANLIB) libmylib$(LIB_EXT)
';
} Убедитесь, что вы используете табуляцию, а не пробелы, в строках, начинающихся с "$(AR)" и "$(RANLIB)". Make не будет работать должным образом, если вы используете пробелы. Также сообщалось, что аргумент "cr" для $(AR) не нужен в системах Win32.
Теперь создадим главные файлы Mytest2 верхнего уровня. Перейдите в каталог выше Mytest2 и выполните следующую команду:
% h2xs -O -n Mytest2 Mytest2/mylib/mylib.h Это выведет предупреждение об перезаписи Mytest2, но это нормально. Наши файлы хранятся в Mytest2/mylib и не будут затронуты.
Обычный Makefile.PL, создаваемый h2xs, не знает о каталоге mylib. Нам нужно сообщить ему, что есть подкаталог и что мы будем генерировать библиотеку в нём. Добавьте аргумент MYEXTLIB к вызову WriteMakefile, чтобы он выглядел так:
WriteMakefile(
NAME => 'Mytest2',
VERSION_FROM => 'Mytest2.pm', # finds $VERSION
LIBS => [''], # e.g., '-lm'
DEFINE => '', # e.g., '-DHAVE_SOMETHING'
INC => '', # e.g., '-I/usr/include/other'
MYEXTLIB => 'mylib/libmylib$(LIB_EXT)',
); и затем в конце добавьте подпрограмму (которая переопределит существующую подпрограмму). Не забудьте использовать символ табуляции для отступа строки, начинающейся с "cd"!
sub MY::postamble {
'
$(MYEXTLIB): mylib/Makefile
cd mylib && $(MAKE) $(PASSTHRU)
';
} Давайте также исправим файл MANIFEST, добавив следующие три строки:
mylib/Makefile.PL
mylib/mylib.c
mylib/mylib.h Для сохранения нашего пространства имён, отредактируйте файл .pm и измените переменную @EXPORT на @EXPORT_OK. Наконец, в файле .xs отредактируйте строку #include, чтобы она выглядела так:
#include "mylib/mylib.h" А также добавьте следующее определение функции в конец файла .xs:
double
foo(a,b,c)
int a
long b
const char * c
OUTPUT:
RETVAL Теперь нам также нужно создать typemap, так как текущий Perl по умолчанию не поддерживает тип const char *. Включите новый раздел TYPEMAP в свой XS-код перед вышеуказанной функцией:
TYPEMAP: <<END
const char * T_PV
END Теперь выполните perl в Makefile.PL верхнего уровня. Обратите внимание, что он также создал Makefile в каталоге mylib. Выполните make и наблюдайте, как он переходит в каталог mylib и выполняет make там тоже.
Теперь отредактируйте скрипт Mytest2.t и измените количество тестов на "5", и добавьте следующие строки в конец скрипта:
is( Mytest2::foo( 1, 2, "Hello, world!" ), 7 );
is( Mytest2::foo( 1, 2, "0.0" ), 7 );
ok( abs( Mytest2::foo( 0, 0, "-3.4" ) - 0.6 ) <= 0.01 ); (При работе с сравнениями чисел с плавающей точкой лучше не проверять на равенство, а проверять, что разница между ожидаемым и фактическим результатом меньше определённого значения (называемого epsilon), которое в данном случае равно 0,01)
Запустите "make test", и всё должно быть хорошо. Есть некоторые предупреждения об отсутствующих тестах для расширения Mytest2::mylib, но их можно проигнорировать.
Что произошло здесь?
В отличие от предыдущих примеров, мы теперь запустили h2xs на реальном файле заголовков. Это привело к появлению дополнительных функций в файлах .pm и .xs.
END_OF_DOCUMENT_MARKER-
В файле .xs теперь используется директива #include с абсолютным путем к файлу заголовка mylib.h. Мы изменили это на относительный путь, чтобы иметь возможность перемещать директорию расширения по желанию.
-
Теперь в файл .xs добавлен новый код на C. Цель процедуры
constant— сделать доступными значения, определенные через #define в файле заголовка, для скрипта Perl (вызовом либоTESTVAL, либо&Mytest2::TESTVAL). Также добавлен код XS для вызовов процедурыconstant. -
В файле .pm изначально экспортировалось имя
TESTVALв массиве@EXPORT. Это могло привести к конфликтам имен. Хорошим правилом является то, что если #define используется только C-процедурами, а не пользователем, то его следует удалить из массива@EXPORT. В качестве альтернативы, если вы не против использования «полного имени» переменной, можно перенести большинство или все элементы из массива@EXPORTв массив@EXPORT_OK. -
Если наш файл заголовка содержал директивы #include, они не обрабатывались утилитой h2xs. На данный момент нет хорошего решения этой проблемы.
-
Мы также сообщили Perl о библиотеке, которую мы построили в подкаталоге mylib. Для этого потребовалось только добавить переменную
MYEXTLIBк вызову WriteMakefile и заменить подпрограмму postamble, чтобы перейти в подкаталог и запустить make. Makefile.PL для библиотеки немного сложнее, но не чрезмерно. Опять же, мы заменили подпрограмму postamble, чтобы вставить свой собственный код. Этот код просто указал, что создаваемая здесь библиотека является статической архивной библиотекой (в отличие от динамически загружаемой библиотеки), и предоставил команды для её построения.
Структура файла .xs
Файл .xs из "ПРИМЕР 4" содержал новые элементы. Для понимания их значения обратите внимание на строку
MODULE = Mytest2 PACKAGE = Mytest2 Всё перед этой строкой — это обычный C-код, описывающий включаемые заголовки и определяющий некоторые вспомогательные функции. Никаких переводов в этой части не выполняется, за исключением того, что встроенная документация POD пропущена (см. perlpod), она переходит в сгенерированный выходной файл C как есть.
Всё после этой строки — описание функций XSUB. Эти описания переводятся xsubpp в C-код, который реализует эти функции с использованием соглашений вызова Perl и делает эти функции видимыми из интерпретатора Perl.
Обратите особое внимание на функцию constant. Это имя встречается дважды в сгенерированном файле .xs: один раз в первой части, как статическая C-функция, а другой раз во второй части, когда определяется интерфейс XSUB для этой статической C-функции.
Это довольно типично для файлов .xs: обычно файл .xs предоставляет интерфейс к существующей C-функции. Затем эта C-функция определена где-то (либо во внешней библиотеке, либо в первой части файла .xs), и Perl-интерфейс к этой функции (т.е. «Perl-склейка») описан во второй части файла .xs. Ситуация в "ПРИМЕРЕ 1", "ПРИМЕРЕ 2" и "ПРИМЕРЕ 3", когда вся работа выполняется внутри «Perl-склейки», является скорее исключением, чем правилом.
Получение минимума из XSUB
Во втором разделе файла .xs из "ПРИМЕРА 4" содержалось следующее описание XSUB:
double
foo(a,b,c)
int a
long b
const char * c
OUTPUT:
RETVAL Обратите внимание, что в отличие от "ПРИМЕРА 1", "ПРИМЕРА 2" и "ПРИМЕРА 3", это описание не содержит фактического кода того, что выполняется при вызове Perl-функции foo(). Чтобы понять, что происходит, можно добавить секцию CODE в этот XSUB:
double
foo(a,b,c)
int a
long b
const char * c
CODE:
RETVAL = foo(a,b,c);
OUTPUT:
RETVAL Однако, эти два XSUB генерируют почти идентичный C-код: компилятор xsubpp достаточно умен, чтобы определить секцию CODE: из первых двух строк описания XSUB. Что насчет секции OUTPUT:? Фактически, это абсолютно то же самое! Секцию OUTPUT: также можно удалить, поскольку секции CODE: или PPCODE: не указаны: xsubpp может понять, что ему нужно сгенерировать секцию вызова функции, и также автоматически сгенерирует секцию OUTPUT. Таким образом, можно упростить XSUB до:
double
foo(a,b,c)
int a
long b
const char * c Можем ли мы сделать то же самое с XSUB
int
is_even(input)
int input
CODE:
RETVAL = (input % 2 == 0);
OUTPUT:
RETVAL из "ПРИМЕРА 2"? Для этого необходимо определить C-функцию int is_even(int input). Как мы видели в "Структуре файла .xs", подходящее место для этого определения — первая часть файла .xs. На самом деле, C-функция
int
is_even(int arg)
{
return (arg % 2 == 0);
} вероятно, избыточна для этого. Достаточно чего-то простого, например, #define:
#define is_even(arg) ((arg) % 2 == 0) После этого в первой части файла .xs, часть «Perl-склейки» становится такой простой
int
is_even(input)
int input Этот метод разделения части «склейки» от рабочей части имеет очевидные компромиссы: если вы хотите изменить Perl-интерфейс, вам нужно изменить два места в вашем коде. Однако он удаляет много беспорядка и делает рабочую часть независимой от особенностей соглашения вызова Perl. (На самом деле, в приведенном описании нет ничего специфического для Perl, другая версия xsubpp могла бы перевести это в TCL-склейку или Python-склейку тоже.)
Больше о параметрах XSUB
С завершением Примера 4 у нас есть простой способ смоделировать реальные библиотеки, чьи интерфейсы могут быть не самыми лучшими. Теперь мы продолжим обсуждение параметров, передаваемых компилятору xsubpp.
Когда вы указываете параметры процедурам в файле .xs, вы на самом деле передаёте три части информации для каждого параметра. Первая часть — это порядок этого параметра относительно других (первый, второй и т.д.). Вторая — тип параметра и состоит из объявления типа параметра (например, int, char*, и т.д.). Третья часть — соглашение вызова для параметра в вызове функции библиотеки.
В то время как Perl передаёт параметры функциям по ссылке, C передаёт параметры по значению; чтобы реализовать C-функцию, которая изменяет данные одного из «параметров», фактический параметр этой C-функции должен быть указателем на данные. Таким образом, две C-функции с объявлениями
int string_length(char *s);
int upper_case_char(char *cp); могут иметь совершенно разные семантики: первая может проверить массив символов, указанный s, а вторая может сразу же разыменовать cp и манипулировать *cp только (используя возвращаемое значение как, скажем, индикатор успеха). Из Perl эти функции будут использоваться совершенно по-разному.
Вы передаёте эту информацию xsubpp, заменив * перед параметром на &. & означает, что параметр должен передаваться функции библиотеки по его адресу. Вышеуказанные две функции могут быть XSUB-ифицированы как
int
string_length(s)
char * s
int
upper_case_char(cp)
char &cp Например, рассмотрим:
int
foo(a,b)
char &a
char * b Первый Perl-параметр этой функции будет обрабатываться как char и присваиваться переменной a, а его адрес будет передаваться в функцию foo. Второй Perl-параметр будет обрабатываться как указатель на строку и присваиваться переменной b. Значение b будет передаваться в функцию foo. Фактический вызов функции foo, который генерирует xsubpp, будет выглядеть так:
foo(&a, b); xsubpp будет анализировать следующие списки аргументов функции идентично:
char &a
char&a
char & a Однако, для лучшего понимания, рекомендуется поместить «&» рядом с именем переменной и подальше от типа переменной, и поместить «*» рядом с типом переменной, но подальше от имени переменной (как в вызове foo выше). Таким образом, легко понять, что именно будет передано C-функции; это будет то, что находится в «последнем столбце».
Вы должны приложить все усилия, чтобы пытаться передавать функции тот тип переменной, который она хочет, когда это возможно. Это сэкономит вам много проблем в долгосрочной перспективе.
Стек аргументов
Если мы посмотрим на любой C-код, сгенерированный любым из примеров, кроме примера 1, мы заметим ряд ссылок на ST(n), где n обычно равно 0. «ST» — это на самом деле макрос, который указывает на n-й параметр в стеке аргументов. ST(0) — это, таким образом, первый параметр в стеке, а значит, и первый параметр, переданный XSUB, ST(1) — второй параметр и так далее.
Когда вы перечисляете аргументы XSUB в файле .xs, это сообщает xsubpp, какой аргумент соответствует какому из стека аргументов (т.е., первый, перечисленный, — это первый параметр и так далее). Вы вызовете катастрофу, если не перечислите их в том же порядке, в каком их ожидает функция.
Фактические значения в стеке аргументов — указатели на значения, переданные в него. Когда аргумент перечислен как значение OUTPUT, соответствующее значение в стеке (т.е., ST(0), если это был первый аргумент) изменяется. Вы можете проверить это, посмотрев на сгенерированный C-код для Примера 3. Код для процедуры round() XSUB содержит строки, похожие на следующие:
double arg = (double)SvNV(ST(0));
/* Round the contents of the variable arg */
sv_setnv(ST(0), (double)arg); Переменная arg изначально устанавливается, беря значение из ST(0), затем сохраняется обратно в ST(0) в конце процедуры.
XSUB также могут возвращать списки, а не только скаляры. Это нужно сделать, манипулируя значениями стека ST(0), ST(1) и т.д., немного по-другому. Подробности см. в perlxs.
XSUB также могут избежать автоматического преобразования аргументов Perl-функции в аргументы C-функции. Подробности см. в perlxs. Некоторые люди предпочитают ручное преобразование, проверяя ST(i) даже в тех случаях, когда автоматическое преобразование сработает, утверждая, что это делает логику вызова XSUB более понятной. Сравните с "Получением минимума из XSUB" для аналогичного компромисса полного разделения «Perl-склейки» и «рабочей» частей XSUB.
Хотя эксперты могут спорить об этих идиомах, новичок в Perl guts может предпочесть способ, который как можно меньше специфичен для Perl-guts, то есть автоматическое преобразование и автоматическое создание вызова, как в "Получении минимума из XSUB". Этот подход также имеет дополнительное преимущество — защиты автора XSUB от будущих изменений API Perl.
Расширение вашего расширения
Иногда вы можете захотеть предоставить дополнительные методы или подпрограммы, чтобы упростить или сделать понятнее интерфейс между Perl и вашим расширением. Эти процедуры должны жить в файле .pm. Их автоматическая загрузка при загрузке самого расширения или только при вызове зависит от того, где в файле .pm находится определение подпрограммы. Также вы можете обратиться к AutoLoader для альтернативного способа хранения и загрузки дополнительных подпрограмм.
Документирование вашего расширения
Нет абсолютно никаких оправданий для того, чтобы не документировать ваше расширение. Документация должна быть в файле .pm. Этот файл будет передан в pod2man, а встроенная документация будет преобразована в формат manpage, затем помещена в директорию blib. Она будет скопирована в директорию manpage Perl при установке расширения.
Вы можете вставлять документацию и Perl-код в файл .pm. На самом деле, если вы хотите использовать автоматическую загрузку методов, вам нужно это сделать, как объясняется в комментарии внутри файла .pm.
См. perlpod для получения дополнительной информации о формате pod.
Установка вашего расширения
После завершения работы расширения и успешного прохождения всех тестов его установка довольно проста: вы просто запускаете «make install». Вам потребуется право записи в каталоги, где установлен Perl, или попросите системного администратора запустить make за вас.
В качестве альтернативы вы можете указать точный каталог для размещения файлов расширения, добавив «PREFIX=/destination/directory» после make install (или между make и install, если у вас устаревшая версия make). Это может быть очень полезно, если вы создаёте расширение, которое в конечном итоге будет распространяться на несколько систем. Затем вы можете просто заархивировать файлы в целевом каталоге и распространить их на целевые системы.
ПРИМЕР 5
В этом примере мы выполним дополнительные действия со стеком аргументов. Все предыдущие примеры возвращали только одно значение. Теперь мы создадим расширение, которое возвращает массив.
Это расширение ориентировано на Unix (struct statfs и системный вызов statfs). Если вы не работаете на системе Unix, вы можете заменить statfs любой другой функцией, возвращающей несколько значений, вы можете жестко закодировать возвращаемые значения вызывающей стороне (хотя это будет немного сложнее проверить случай ошибки), или вы можете просто не выполнять этот пример. Если вы изменяете XSUB, обязательно исправьте тестовые случаи для соответствия изменениям.
Возвращайтесь в каталог Mytest и добавьте следующий код в конец файла Mytest.xs:
void
statfs(path)
char * path
INIT:
int i;
struct statfs buf;
PPCODE:
i = statfs(path, &buf);
if (i == 0) {
XPUSHs(sv_2mortal(newSVnv(buf.f_bavail)));
XPUSHs(sv_2mortal(newSVnv(buf.f_bfree)));
XPUSHs(sv_2mortal(newSVnv(buf.f_blocks)));
XPUSHs(sv_2mortal(newSVnv(buf.f_bsize)));
XPUSHs(sv_2mortal(newSVnv(buf.f_ffree)));
XPUSHs(sv_2mortal(newSVnv(buf.f_files)));
XPUSHs(sv_2mortal(newSVnv(buf.f_type)));
} else {
XPUSHs(sv_2mortal(newSVnv(errno)));
} Вам также нужно добавить следующий код в начало файла .xs, сразу после включения "XSUB.h":
#include <sys/vfs.h> Также добавьте следующий фрагмент кода в Mytest.t, увеличив количество тестов с «9» до «11»:
my @a;
@a = Mytest::statfs("/blech");
ok( scalar(@a) == 1 && $a[0] == 2 );
@a = Mytest::statfs("/");
is( scalar(@a), 7 ); Новые элементы в этом примере
В этом примере добавлено несколько новых концепций. Мы рассмотрим их по очереди.
-
Директива INIT содержит код, который будет помещён сразу после декодирования стека аргументов. C не допускает объявления переменных в произвольных местах внутри функции, поэтому это обычно лучший способ объявить локальные переменные, необходимые XSUB. (В качестве альтернативы можно поместить весь раздел
PPCODE:в фигурные скобки и поместить эти объявления вверху.) -
Эта процедура также возвращает разное количество аргументов в зависимости от успеха или неудачи вызова statfs. Если произошла ошибка, возвращается число ошибки в виде массива с одним элементом. Если вызов выполнен успешно, возвращается массив из 7 элементов. Поскольку в эту функцию передаётся только один аргумент, нам нужно место в стеке для хранения 7 возможных возвращаемых значений.
Мы делаем это, используя директиву PPCODE:, а не директиву CODE:. Это сообщает xsubpp, что мы будем управлять возвращаемыми значениями, которые будут помещены в стек аргументов сами.
-
Для помещения значений, которые должны быть возвращены вызывающей программе, в стек мы используем серию макросов, начинающихся с «XPUSH». Существует пять различных версий для помещения целых чисел, беззнаковых целых чисел, чисел с плавающей точкой, строк и скаляров Perl в стек. В нашем примере мы поместили Perl-скаляр в стек. (На самом деле это единственный макрос, который можно использовать для возврата нескольких значений.)
Макросы XPUSH* автоматически расширяют стек, чтобы предотвратить его переполнение. Вы помещаете значения в стек в том порядке, в котором вы хотите, чтобы они отображались вызывающей программой.
-
Значения, помещённые в возвратный стек XSUB, фактически являются mortal SV. Они делают их mortal, чтобы после того, как вызывающая программа скопирует значения, SV, содержащие возвращаемые значения, можно было освободить. Если бы они не были mortal, они продолжали бы существовать после возврата процедуры XSUB, но не были бы доступны. Это утечка памяти.
-
Если мы заинтересованы в производительности, а не в компактности кода, в успешно выполненном случае мы бы не использовали макросы
XPUSHs, а макросыPUSHs, и предварительно расширили бы стек перед помещением возвращаемых значений:EXTEND(SP, 7);Компромисс заключается в том, что необходимо заранее вычислить количество возвращаемых значений (хотя избыточное расширение стека, как правило, не причиняет вреда, кроме потребления памяти).
Аналогично, в случае неудачи мы могли бы использовать
PUSHsбез расширения стека: ссылка на Perl-функцию попадает в стек XSUB, таким образом, стек всегда достаточно велик для одного возвращаемого значения.
ПРИМЕР 6
В этом примере мы будем принимать ссылку на массив в качестве входного параметра и возвращать ссылку на массив хешей. Это продемонстрирует манипуляцию сложными типами данных Perl из XSUB.
Это расширение несколько искусственное. Оно основано на коде в предыдущем примере. Оно многократно вызывает функцию statfs, принимая ссылку на массив имён файлов в качестве входных данных и возвращая ссылку на массив хешей, содержащих данные для каждого из файловых систем.
Возвращайтесь в каталог Mytest и добавьте следующий код в конец файла Mytest.xs:
SV *
multi_statfs(paths)
SV * paths
INIT:
AV * results;
SSize_t numpaths = 0, n;
int i;
struct statfs buf;
SvGETMAGIC(paths);
if ((!SvROK(paths))
|| (SvTYPE(SvRV(paths)) != SVt_PVAV)
|| ((numpaths = av_top_index((AV *)SvRV(paths))) < 0))
{
XSRETURN_UNDEF;
}
results = (AV *)sv_2mortal((SV *)newAV());
CODE:
for (n = 0; n <= numpaths; n++) {
HV * rh;
STRLEN l;
SV * path = *av_fetch((AV *)SvRV(paths), n, 0);
char * fn = SvPVbyte(path, l);
i = statfs(fn, &buf);
if (i != 0) {
av_push(results, newSVnv(errno));
continue;
}
rh = (HV *)sv_2mortal((SV *)newHV());
hv_store(rh, "f_bavail", 8, newSVnv(buf.f_bavail), 0);
hv_store(rh, "f_bfree", 7, newSVnv(buf.f_bfree), 0);
hv_store(rh, "f_blocks", 8, newSVnv(buf.f_blocks), 0);
hv_store(rh, "f_bsize", 7, newSVnv(buf.f_bsize), 0);
hv_store(rh, "f_ffree", 7, newSVnv(buf.f_ffree), 0);
hv_store(rh, "f_files", 7, newSVnv(buf.f_files), 0);
hv_store(rh, "f_type", 6, newSVnv(buf.f_type), 0);
av_push(results, newRV_inc((SV *)rh));
}
RETVAL = newRV_inc((SV *)results);
OUTPUT:
RETVAL И добавьте следующий код в Mytest.t, увеличив количество тестов с «11» до «13»:
my $results = Mytest::multi_statfs([ '/', '/blech' ]);
ok( ref $results->[0] );
ok( ! ref $results->[1] ); Новые элементы в этом примере
Здесь введено несколько новых концепций, описанных ниже:
-
Эта функция не использует typemap. Вместо этого мы объявляем её как принимающую один параметр SV* (скаляр) и возвращающую значение SV*, и мы сами позаботимся о заполнении этих скаляров в коде. Поскольку мы возвращаем только одно значение, нам не нужна директива
PPCODE:— вместо этого мы используем директивыCODE:иOUTPUT:. -
При работе со ссылками важно обращаться с ними осторожно. Блок
INIT:сначала вызывает SvGETMAGIC(paths), на случай если paths — привязанная переменная. Затем он проверяет, чтоSvROKвозвращает true, что указывает на то, что paths — действительная ссылка. (Простая проверкаSvROKне сработает для привязанной переменной.) Затем он проверяет, что объект, на который ссылается paths, является массивом, используяSvRVдля разыменования paths иSvTYPEдля определения его типа. В качестве дополнительной проверки он проверяет, что массив, на который ссылается paths, не пустой, используя функциюav_top_index(которая возвращает -1, если массив пустой). Макрос XSRETURN_UNDEF используется для прерывания XSUB и возвращения неопределённого значения, когда не выполняются все три условия. -
В этом XSUB мы манипулируем несколькими массивами. Обратите внимание, что массив внутренне представлен указателем AV*. Функции и макросы для работы с массивами аналогичны функциям в Perl:
av_top_indexвозвращает индекс последнего элемента в AV*, подобно $#array;av_fetchизвлекает значение отдельного скаляра из массива по его индексу;av_pushдобавляет скалярное значение в конец массива, автоматически расширяя массив при необходимости.В частности, мы читаем имена путей один за другим из входного массива и сохраняем результаты в выходном массиве (results) в том же порядке. Если statfs завершается ошибкой, элементом, добавленным в возвращаемый массив, становится значение errno после ошибки. Однако если statfs выполняется успешно, добавленным в возвращаемый массив элементом будет ссылка на хеш, содержащий некоторые данные из структуры statfs.
Как и в случае со стеком возврата, было бы возможно (и небольшим выигрышем в производительности) предварительно расширить возвращаемый массив перед помещением в него данных, поскольку мы знаем, сколько элементов мы вернём:
av_extend(results, numpaths); -
В этой функции мы выполняем только одну операцию с хешем, которая состоит в хранении нового скаляра под ключом с помощью
hv_store. Хеш представлен указателем HV*. Как и массивы, функции для работы с хешами из XSUB отражают функциональность, доступную из Perl. Подробнее см. perlguts и perlapi. -
Для создания ссылки мы используем функцию
newRV_inc. Обратите внимание, что в этом случае (и во многих других) вы можете привести AV* или HV* к типу SV*. Это позволяет вам получать ссылки на массивы, хеши и скаляры с помощью одной и той же функции. Напротив, функцияSvRVвсегда возвращает SV*, который может потребоваться привести к соответствующему типу, если это что-то другое, кроме скаляра (проверьте с помощьюSvTYPE). -
На данном этапе xsubpp выполняет очень мало работы — различия между Mytest.xs и Mytest.c минимальны.
ПРИМЕР 7 (Скоро)
XPUSH аргументы и установка RETVAL и назначение возвращаемого значения массиву
ПРИМЕР 8 (Скоро)
Указание $!
ПРИМЕР 9 Передача открытых файлов в XSes
Вы могли бы подумать, что передача файлов в XS сложна из-за всех typeglobs и т. д. Но это не так.
Предположим, что по какой-то странной причине нам нужен оболочка для стандартной функции библиотеки C fputs(). Вот всё, что нужно:
#define PERLIO_NOT_STDIO 0 /* For co-existence with stdio only */
#define PERL_NO_GET_CONTEXT /* This is more efficient */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <stdio.h>
int
fputs(s, stream)
char * s
FILE * stream Реальная работа выполняется в стандартном typemap.
Для получения дополнительной информации см. "Совместное использование с stdio" в perlapio.
Но вы теряете все преимущества, которые предоставляют слои perlio. Это вызывает функцию stdio fputs(), которая ничего не знает об этих слоях.
Стандартный typemap предлагает три варианта PerlIO *: InputStream (T_IN), InOutStream (T_INOUT) и OutputStream (T_OUT). Чистый PerlIO * считается T_INOUT. Если это важно в вашем коде (см. ниже, почему это может быть важно), определите или переопределите одно из конкретных имён и используйте его как тип аргумента или результата в вашем файле XS.
Стандартный typemap не содержит PerlIO * до Perl 5.7, но он имеет три варианта потоков. Прямое использование PerlIO * не является обратной совместимостью, если вы не предоставите свой собственный typemap.
Для потоков, поступающих из Perl, основное отличие заключается в том, что OutputStream получит выходной PerlIO * — что может иметь значение при работе с сокетом. Как и в нашем примере…
Для потоков, передаваемых в Perl, создаётся новый дескриптор файла (то есть ссылка на новый glob) и связывается с предоставленным PerlIO *. Если состояние чтения/записи PerlIO * неверно, вы можете получить ошибки или предупреждения при использовании дескриптора файла. Поэтому, если вы открыли PerlIO * как «w», то он должен быть OutputStream, если он открыт как «r», то он должен быть InputStream.
Теперь предположим, что вы хотите использовать слои perlio в своём XS. Мы будем использовать функцию perlio PerlIO_puts() в качестве примера.
В C-части файла XS (над первой строкой MODULE) у вас есть
#define OutputStream PerlIO *
or
typedef PerlIO * OutputStream; И вот код XS:
int
perlioputs(s, stream)
char * s
OutputStream stream
CODE:
RETVAL = PerlIO_puts(stream, s);
OUTPUT:
RETVAL Нам нужно использовать раздел CODE, потому что у PerlIO_puts() аргументы переставлены по сравнению с fputs(), и мы хотим сохранить одинаковый порядок аргументов.
Стремясь тщательно изучить этот вопрос, мы хотим использовать stdio fputs() на PerlIO *. Это означает, что мы должны запросить у системы perlio stdio FILE *.
int
perliofputs(s, stream)
char * s
OutputStream stream
PREINIT:
FILE *fp = PerlIO_findFILE(stream);
CODE:
if (fp != (FILE*) 0) {
RETVAL = fputs(s, fp);
} else {
RETVAL = -1;
}
OUTPUT:
RETVALПримечание: PerlIO_findFILE() будет искать слой stdio. Если он не найдет его, он вызовет PerlIO_exportFILE() для создания нового слоя stdio FILE. Пожалуйста, вызывайте PerlIO_exportFILE() только если вам нужен новый FILE. Он будет генерировать его при каждом вызове и добавлять новый слой stdio. Поэтому не вызывайте его повторно для одного и того же файла. PerlIO_findFILE() извлечет слой stdio после его создания PerlIO_exportFILE().
Это относится только к системе perlio. Для версий до 5.7, PerlIO_exportFILE() эквивалентно PerlIO_findFILE().
Решение проблем с этими примерами
Как упоминалось в начале этого документа, если у вас возникли проблемы с этими примерами расширений, вы можете посмотреть, поможет ли что-нибудь из этого.
-
В версиях 5.002 до версии гамма, тестовый скрипт в Примере 1 не будет работать должным образом. Вам нужно изменить строку "use lib" на:
use lib './blib'; -
В версиях 5.002 до версии 5.002b1h, файл test.pl не создавался автоматически h2xs. Это означает, что вы не можете сказать "make test" для запуска тестового скрипта. Вам нужно добавить следующую строку перед инструкцией "use extension":
use lib './blib'; -
В версиях 5.000 и 5.001 вместо вышеуказанной строки вам нужно использовать следующую строку:
BEGIN { unshift(@INC, "./blib") } -
В данном документе предполагается, что исполняемый файл под названием "perl" — это Perl версии 5. Некоторые системы могут иметь Perl версии 5, установленный как "perl5".
См. также
Для получения дополнительной информации обратитесь к perlguts, perlapi, perlxs, perlmod, perlapio и perlpod
Автор
Jeff Okamoto <okamoto@corp.hp.com>
Проверено и помогали Dean Roehrich, Ilya Zakharevich, Andreas Koenig и Tim Bunce.
Материал по PerlIO предоставлен Lupe Christoph, с некоторыми уточнениями Nick Ing-Simmons.
Изменения для h2xs начиная с Perl 5.8.x от Renee Baecker
В настоящее время этот документ поддерживается как часть самого Perl.
Дата последнего изменения
2020-10-05
© 1993–2023 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.38.0/perlxstut