perlxs
СОДЕРЖАНИЕ
- ИМЯ
- ОПИСАНИЕ
- Введение
- В пути
- Структура XSUB
- Стек аргументов
- Переменная RETVAL
- Возврат SVs, AVs и HVs через RETVAL
- Ключевое слово MODULE
- Ключевое слово PACKAGE
- Ключевое слово PREFIX
- Ключевое слово OUTPUT:
- Ключевое слово NO_OUTPUT
- Ключевое слово CODE:
- Ключевое слово INIT:
- Ключевое слово NO_INIT
- Ключевое слово TYPEMAP:
- Инициализация параметров функции
- Значения параметров по умолчанию
- Ключевое слово PREINIT:
- Ключевое слово SCOPE:
- Ключевое слово INPUT:
- Ключевые слова IN/OUTLIST/IN_OUTLIST/OUT/IN_OUT
- Ключевое слово length(NAME)
- Списки параметров переменной длины
- Ключевое слово C_ARGS:
- Ключевое слово PPCODE:
- Возврат undef и пустых списков
- Ключевое слово REQUIRE:
- Ключевое слово CLEANUP:
- Ключевое слово POSTCALL:
- Ключевое слово BOOT:
- Ключевое слово VERSIONCHECK:
- Ключевое слово PROTOTYPES:
- Ключевое слово PROTOTYPE:
- Ключевое слово ALIAS:
- Ключевое слово OVERLOAD:
- Ключевое слово FALLBACK:
- Ключевое слово INTERFACE:
- Ключевое слово INTERFACE_MACRO:
- Ключевое слово INCLUDE:
- Ключевое слово INCLUDE_COMMAND:
- Ключевое слово CASE:
- Ключевое слово EXPORT_XSUB_SYMBOLS:
- Унарный оператор &
- Вставка POD, комментариев и директив препроцессора C
- Использование XS с C++
- Стратегия интерфейса
- Perl-объекты и C-структуры
- Безопасное хранение статических данных в XS
- Интерфейсы систем, учитывающие потоки
- ПРИМЕРЫ
- ОСОБЕННОСТИ
- ВЕРСИЯ XS
- АВТОР
ИМЯ
perlxs - Справочник по языку XS
ОПИСАНИЕ
Введение
XS — это формат файла описания интерфейса, используемый для создания интерфейса расширения между Perl и кодом C (или C библиотекой), который вы хотите использовать с Perl. Интерфейс XS комбинируется с библиотекой для создания новой библиотеки, которую затем можно загрузить динамически или статически связать с perl. Описание интерфейса XS написано на языке XS и является основной частью интерфейса расширения Perl.
Перед написанием XS прочитайте раздел "ОСОБЕННОСТИ" ниже.
XSUB формирует базовый элемент интерфейса XS. После компиляции с помощью компилятора xsubpp каждый XSUB соответствует определению C функции, которая обеспечит связь между соглашениями о вызовах Perl и соглашениями о вызовах C.
Код связи извлекает аргументы из стека Perl, преобразует эти значения Perl в форматы, ожидаемые функцией C, вызывает эту функцию C, передает значения возврата функции C обратно в Perl. Значения возврата здесь могут быть обычным значением возврата C или любыми аргументами функции C, которые могут служить параметрами вывода. Эти значения возврата могут быть переданы обратно в Perl, поместив их в стек Perl или изменив предоставленные со стороны Perl аргументы.
Это несколько упрощенное представление того, что происходит на самом деле. Поскольку Perl допускает более гибкие соглашения о вызовах, чем C, XSUB могут выполнять гораздо больше на практике, например, проверять входные параметры на валидность, генерировать исключения (или возвращать undef/пустой список) если значение возврата функции C указывает на ошибку, вызывать различные функции C в зависимости от количества и типов аргументов, предоставлять объектно-ориентированный интерфейс и т.д.
Конечно, можно было бы написать такой код связи непосредственно на C. Однако это была бы трудоемкая задача, особенно если необходимо писать код связи для нескольких функций C и/или если вы недостаточно знакомы с дисциплиной стека Perl и другими подобными тонкостями. XS приходит на помощь в этом случае: вместо написания этого кода связи на C вручную, можно написать более краткое описание того, что должно быть сделано кодом связи, и позволить компилятору XS xsubpp сделать все остальное.
Язык XS позволяет описать соответствие между тем, как используется процедура C, и тем, как используется соответствующая процедура Perl. Он также позволяет создавать процедуры Perl, которые непосредственно переводятся в код C и не связаны с предварительно существующей функцией C. В тех случаях, когда C интерфейс совпадает с Perl интерфейсом, объявление XSUB почти идентично объявлению функции C (в стиле K&R). В таких ситуациях есть еще один инструмент, называемый h2xs, который способен перевести весь файл заголовков C в соответствующий файл XS, который обеспечит связь с функциями/макросами, описанными в файле заголовков.
Компилятор XS называется xsubpp. Этот компилятор создает конструкции, необходимые для того, чтобы XSUB мог манипулировать значениями Perl, и создает необходимую связь для вызова XSUB из Perl. Компилятор использует typemaps для определения того, как сопоставлять параметры функции C и значения вывода со значениями Perl и обратно. Типовое сопоставление (которое поставляется с Perl) обрабатывает многие распространенные типы C. Может потребоваться дополнительное сопоставление типов для обработки любых специальных структур и типов для связанной библиотеки. Для получения дополнительной информации о typemaps см. perlxstypemap.
Файл в формате XS начинается с раздела языка C, который продолжается до первой директивы MODULE =. После этой строки могут следовать другие директивы XS и определения XSUB. «Язык», используемый в этой части файла, обычно называется языком XS. xsubpp распознает и пропускает POD (см. perlpod) как в разделах языка C, так и в разделах языка XS, что позволяет файлу XS содержать встроенную документацию.
См. perlxstut для получения руководства по всему процессу создания расширений.
Примечание: для некоторых расширений система SWIG Дэвида Бисли может обеспечить значительно более удобный механизм для создания кода связи расширений. См. http://www.swig.org/ для получения дополнительной информации.
В пути
Многие из последующих примеров будут сосредоточены на создании интерфейса между Perl и функциями библиотеки ONC+ RPC bind. Функция rpcb_gettime() используется для демонстрации многих функций языка XS. Эта функция имеет два параметра; первый — входной параметр, а второй — выходной параметр. Функция также возвращает значение состояния.
bool_t rpcb_gettime(const char *host, time_t *timep); Из C эта функция будет вызываться следующими операторами.
#include <rpc/rpc.h>
bool_t status;
time_t timep;
status = rpcb_gettime( "localhost", &timep ); Если создается XSUB для прямого преобразования между этой функцией и Perl, то этот XSUB будет использоваться из Perl со следующим кодом. Переменные $status и $timep будут содержать результат функции.
use RPC;
$status = rpcb_gettime( "localhost", $timep ); Следующий файл XS демонстрирует подпрограмму XS или XSUB, которая показывает один из возможных интерфейсов с функцией rpcb_gettime(). Этот XSUB представляет собой прямое преобразование между C и Perl, и поэтому сохраняет интерфейс даже из Perl. Этот XSUB будет вызываться из Perl с использованием показанного выше способа. Обратите внимание, что первые три инструкции #include, для EXTERN.h, perl.h и XSUB.h, всегда будут присутствовать в начале файла XS. Этот подход и другие будут расширены позже в этом документе. #define для PERL_NO_GET_CONTEXT должен быть доступен для более эффективного получения контекста интерпретатора, см. perlguts для получения подробностей.
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <rpc/rpc.h>
MODULE = RPC PACKAGE = RPC
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep Любое расширение Perl, включая те, которые содержат XSUB, должно иметь модуль Perl, который будет служить загрузчиком, подключающим расширение к Perl. Этот модуль будет экспортировать функции и переменные расширения в программу Perl и вызовет подключение XSUB расширения к Perl. Следующий модуль будет использоваться в большинстве примеров в этом документе и должен использоваться из Perl с командой use, как показано ранее. Модули Perl будут объяснены более подробно позже в этом документе.
package RPC;
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw( rpcb_gettime );
bootstrap RPC;
1; В этом документе будут рассмотрены различные интерфейсы к XSUB rpcb_gettime(). XSUB будут принимать параметры в разном порядке или с различным количеством параметров. В каждом случае XSUB является абстракцией между Perl и реальной функцией C rpcb_gettime(), и XSUB должен всегда гарантировать, что реальная функция C rpcb_gettime() вызывается с правильными параметрами. Эта абстракция позволит программисту создать более Perl-подобный интерфейс к функции C.
Структура XSUB
Простейшие XSUB состоят из 3 частей: описание возвращаемого значения, имя XSUB-процедуры и имена её аргументов, и описание типов или форматов аргументов.
Следующий XSUB позволяет программе Perl получить доступ к функции C-библиотеки sin(). XSUB будет имитировать функцию C, которая принимает один аргумент и возвращает одно значение.
double
sin(x)
double x По желанию, можно объединить описание типов и список имён аргументов, переписав это как
double
sin(double x) Это делает этот XSUB похожим на объявление ANSI C. После списка аргументов допускается необязательная точка с запятой, как в
double
sin(double x); Параметры с типами указателей C могут иметь различную семантику: функции C с похожими объявлениями
bool string_looks_as_a_number(char *s);
bool make_char_uppercase(char *c); используются абсолютно несовместимым образом. Параметры этих функций могут быть описаны xsubpp так:
char * s
char &c Оба этих объявления XS соответствуют типу C char*, но они имеют различную семантику, см. "Оператор &".
Удобно считать, что оператор косвенного обращения * следует рассматривать как часть типа, а оператор взятия адреса & следует рассматривать как часть переменной. См. perlxstypemap для получения дополнительной информации об обработке квалификаторов и унарных операторов в типах C.
Имя функции и тип возвращаемого значения должны быть размещены на отдельных строках и должны быть выровнены по левому краю.
INCORRECT CORRECT
double sin(x) double
double x sin(x)
double x Остальная часть описания функции может быть отступающей или выровненной по левому краю. Следующий пример показывает функцию с её телом, выровненным по левому краю. В большинстве примеров в этом документе тело будет отступать для лучшей читаемости.
CORRECT
double
sin(x)
double x Более сложные XSUB могут содержать много других разделов. Каждый раздел XSUB начинается с соответствующего ключевого слова, такого как INIT: или CLEANUP:. Однако, первые две строки XSUB всегда содержат одни и те же данные: описания типа возвращаемого значения и имён функции и её параметров. Всё, что следует за ними, считается разделом INPUT:, если явно не помечено другим ключевым словом. (См. "Ключевое слово INPUT:".)
Раздел XSUB продолжается до тех пор, пока не будет найдено другое ключевое слово начала раздела.
Стек аргументов
Стек аргументов Perl используется для хранения значений, которые передаются в качестве параметров XSUB, и для хранения возвращаемого значения(й) XSUB. На самом деле все функции Perl (включая не-XSUB) хранят свои значения в этом стеке всё то же время, каждое ограничено своим диапазоном позиций в стеке. В этом документе первая позиция в этом стеке, которая принадлежит активной функции, будет обозначаться как позиция 0 для этой функции.
XSUB ссылаются на свои аргументы стека с макросом ST(x), где x относится к позиции в части стека этого XSUB. Позиция 0 для этой функции будет известна XSUB как ST(0). Входящие параметры XSUB и возвращаемые значения всегда начинаются с ST(0). В многих простых случаях компилятор xsubpp сгенерирует код, необходимый для обработки стека аргументов, встраивая фрагменты кода, найденные в typemap. В более сложных случаях программист должен предоставить код.
Переменная RETVAL
Переменная RETVAL — это специальная переменная C, которая объявляется автоматически для вас. Тип C переменной RETVAL соответствует типу возвращаемого значения функции C-библиотеки. Компилятор xsubpp объявит эту переменную в каждом XSUB с не-void типом возвращаемого значения. По умолчанию сгенерированная функция C будет использовать RETVAL для хранения возвращаемого значения вызываемой функции C-библиотеки. В простых случаях значение RETVAL будет помещено в ST(0) стека аргументов, где оно может быть получено Perl как возвращаемое значение XSUB.
Если у XSUB тип возвращаемого значения void, то компилятор не объявит переменную RETVAL для этой функции. При использовании раздела PPCODE: нет необходимости в манипулировании переменной RETVAL, раздел может использовать прямую обработку стека для размещения выходных значений в стеке.
Если директива PPCODE: не используется, void возвращаемое значение должно использоваться только для подпрограмм, которые не возвращают значения, даже если используется директива CODE:, которая явно устанавливает ST(0).
Старые версии этого документа рекомендовали использование void возвращаемого значения в таких случаях. Было обнаружено, что это может привести к segfaults в тех случаях, когда XSUB был на самом деле void. Эта практика теперь устарела и может не поддерживаться в будущих версиях. Используйте возвращаемое значение SV * в таких случаях. (В настоящее время xsubpp содержит некоторый эвристический код, который пытается различить «истинно-пустые» и «функции, объявленные как пустые по старой практике». Таким образом, ваш код зависит от этой эвристики, если вы не используете SV * как возвращаемое значение.)
Возвращение SVs, AV и HVs через RETVAL
Когда вы используете RETVAL для возврата SV *, за кулисами происходит некоторое волшебство, которое следует упомянуть. Когда вы манипулируете стеком аргументов с помощью макроса ST(x), например, вы обычно должны уделять особое внимание счёту ссылок. (Для получения дополнительной информации о счёте ссылок см. perlguts.) Чтобы облегчить вам жизнь, файл typemap автоматически делает RETVAL временным, когда вы возвращаете SV *. Таким образом, следующие два XSUB более или менее эквивалентны:
void
alpha()
PPCODE:
ST(0) = newSVpv("Hello World",0);
sv_2mortal(ST(0));
XSRETURN(1);
SV *
beta()
CODE:
RETVAL = newSVpv("Hello World",0);
OUTPUT:
RETVAL Это довольно полезно, так как обычно улучшает читаемость. В то время как это работает нормально для SV *, к сожалению, не так просто сделать AV * или HV * значением возвращаемого значения. Вы должны быть в состоянии написать:
AV *
array()
CODE:
RETVAL = newAV();
/* do something with RETVAL */
OUTPUT:
RETVAL Но из-за неисправимой ошибки (исправление её сломало бы множество существующих модулей CPAN) в файле typemap счётчик ссылок AV * не корректно уменьшается. Таким образом, вышеуказанный XSUB будет утечки памяти всякий раз, когда он вызывается. Та же проблема существует для HV *, CV * и SVREF (что указывает на скалярную ссылку, а не на общую SV *). В коде XS в perls, начиная с perl 5.16, вы можете переопределить typemaps для любого из этих типов с версией, которая имеет надлежащую обработку refcounts. В вашем разделе TYPEMAP сделайте
AV* T_AVREF_REFCOUNT_FIXED чтобы получить исправленную версию. Для обратной совместимости со старыми версиями perl, вы можете вместо этого вручную уменьшать счётчик ссылок при возвращении одного из перечисленных типов, используя sv_2mortal:
AV *
array()
CODE:
RETVAL = newAV();
sv_2mortal((SV*)RETVAL);
/* do something with RETVAL */
OUTPUT:
RETVAL Помните, что вам не нужно делать это для SV *. Справочная документация по всем core typemaps находится в perlxstypemap.
Ключевое слово MODULE
Ключевое слово MODULE используется для запуска кода XS и для указания пакета функций, которые определяются. Весь текст, предшествующий первому ключевому слову MODULE, рассматривается как код C и передаётся в выходные данные с удалённым POD, но в остальном нетронутым. Каждый модуль XS будет иметь функцию загрузки, которая используется для подключения XSUB к Perl. Имя пакета этой функции загрузки будет соответствовать значению последнего оператора MODULE в файлах исходного кода XS. Значение MODULE должно всегда оставаться постоянным в одном файле XS, хотя это не обязательно.
Следующий пример запустит код XS и поместит все функции в пакет с именем RPC.
MODULE = RPC Ключевое слово PACKAGE
Когда функции внутри файла исходного кода XS должны быть разделены на пакеты, следует использовать ключевое слово PACKAGE. Это ключевое слово используется вместе с ключевым словом MODULE и должно следовать непосредственно за ним при использовании.
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ]
MODULE = RPC PACKAGE = RPCB
[ XS code in package RPCB ]
MODULE = RPC PACKAGE = RPC
[ XS code in package RPC ] Одно и то же имя пакета может использоваться более одного раза, что позволяет создавать несмежные фрагменты кода. Это полезно, если у вас есть более сильный принцип упорядочивания, чем имена пакетов.
Хотя это ключевое слово является необязательным и в некоторых случаях предоставляет избыточную информацию, оно должно всегда использоваться. Это ключевое слово обеспечит, что XSUB появятся в нужном пакете.
Ключевое слово PREFIX
Ключевое слово PREFIX обозначает префиксы, которые должны быть удалены из имён функций Perl. Если C функция является rpcb_gettime(), а значение PREFIX — rpcb_, тогда Perl увидит эту функцию как gettime().
Это ключевое слово должно следовать за ключевым словом PACKAGE при использовании. Если PACKAGE не используется, PREFIX должно следовать за ключевым словом MODULE.
MODULE = RPC PREFIX = rpc_
MODULE = RPC PACKAGE = RPCB PREFIX = rpcb_ Ключевое слово OUTPUT:
Ключевое слово OUTPUT: указывает, что определённые параметры функции должны обновляться (новые значения становятся видимыми для Perl) при завершении XSUB или что определённые значения должны быть возвращены вызывающей функции Perl. Для простых функций, которые не имеют разделов CODE: или PPCODE:, таких как функция sin() выше, переменная RETVAL автоматически обозначается как выходное значение. Для более сложных функций компилятору xsubpp потребуется помощь для определения, какие переменные являются выходными переменными.
Это ключевое слово обычно используется для дополнения ключевого слова CODE:. Переменная RETVAL не распознаётся как выходная переменная, когда присутствует ключевое слово CODE:. Ключевое слово OUTPUT: используется в этой ситуации, чтобы сообщить компилятору, что RETVAL действительно является выходной переменной.
Ключевое слово OUTPUT: также может использоваться для указания того, что параметры функций являются выходными переменными. Это может быть необходимо, когда параметр был изменён внутри функции, и программист хочет, чтобы обновление было видно Perl.
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep Ключевое слово OUTPUT: также позволит сопоставить выходной параметр с соответствующим фрагментом кода, а не с typemap.
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep sv_setnv(ST(1), (double)timep); xsubpp генерирует автоматическое SvSETMAGIC() для всех параметров в разделе OUTPUT XSUB, кроме RETVAL. Это обычно желаемое поведение, так как оно обеспечивает правильное вызов магии 'set' для выходных параметров (необходимо для параметров хешей или массивов, которые должны быть созданы, если они не существовали). Если по какой-то причине это поведение нежелательно, в раздел OUTPUT можно добавить строку SETMAGIC: DISABLE, чтобы отключить его для оставшихся параметров в разделе OUTPUT. Аналогично, SETMAGIC: ENABLE можно использовать для повторного включения его для оставшейся части раздела OUTPUT. Более подробную информацию о магии 'set' см. в perlguts.
Ключевое слово NO_OUTPUT
Ключевое слово NO_OUTPUT может быть размещено в качестве первого токена XSUB. Это ключевое слово указывает, что, хотя C-подпрограмма, к которой мы предоставляем интерфейс, имеет тип возвращаемого значения, отличного от void, значение возврата этой C-подпрограммы не должно возвращаться из сгенерированной Perl-подпрограммы.
При наличии этого ключевого слова создается "Переменная RETVAL", и в сгенерированном вызове подпрограммы эта переменная присваивается, но значение этой переменной не будет использоваться в автоматически сгенерированном коде.
Это ключевое слово имеет смысл только в том случае, если RETVAL будет обработано кодом, предоставленным пользователем. Оно особенно полезно для создания более Perl-подобного интерфейса функции, особенно когда значение возврата C является просто индикатором состояния ошибки. Например,
NO_OUTPUT int
delete_file(char *name)
POSTCALL:
if (RETVAL != 0)
croak("Error %d while deleting file '%s'", RETVAL, name); Здесь сгенерированная XS-функция ничего не возвращает при успешном выполнении и завершается с осмысленным сообщением об ошибке при ошибке.
Ключевое слово CODE:
Это ключевое слово используется в более сложных XSUB, которые требуют специальной обработки C-функции. Переменная RETVAL все еще объявляется, но она не будет возвращена, если это не указано в разделе OUTPUT:.
Следующий XSUB предназначен для C-функции, которая требует специальной обработки ее параметров. Сначала представлено использование Perl.
$status = rpcb_gettime( "localhost", $timep ); Следующий XSUB.
bool_t
rpcb_gettime(host,timep)
char *host
time_t timep
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL Ключевое слово INIT:
Ключевое слово INIT: позволяет вставить инициализацию в XSUB перед тем, как компилятор сгенерирует вызов C-функции. В отличие от ключевого слова CODE: выше, это ключевое слово не влияет на способ обработки компилятором RETVAL.
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
INIT:
printf("# Host is %s\n", host );
OUTPUT:
timep Другое использование раздела INIT: - проверка предопределенных условий перед вызовом C-функции:
long long
lldiv(a,b)
long long a
long long b
INIT:
if (a == 0 && b == 0)
XSRETURN_UNDEF;
if (b == 0)
croak("lldiv: cannot divide by 0"); Ключевое слово NO_INIT
Ключевое слово NO_INIT используется для указания того, что параметр функции используется только как выходное значение. Компилятор xsubpp обычно генерирует код для чтения значений всех параметров функции из стека аргументов и присваивает их переменным C при входе в функцию. NO_INIT сообщит компилятору, что некоторые параметры будут использоваться для вывода, а не для ввода, и что они будут обработаны до завершения функции.
Следующий пример показывает вариацию функции rpcb_gettime(). Эта функция использует переменную timep только как выходную переменную и не заботится о ее начальном содержании.
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep = NO_INIT
OUTPUT:
timep Ключевое слово TYPEMAP:
Начиная с Perl 5.16, вы можете встраивать typemaps в свой XS-код вместо или в дополнение к typemaps в отдельном файле. Несколько таких встроенных typemaps будут обрабатываться в порядке появления в XS-коде и, как локальные typemap-файлы, имеют приоритет над стандартным typemap, встроенные typemaps могут перезаписывать предыдущие определения разделов TYPEMAP, INPUT и OUTPUT. Синтаксис встроенных typemaps
TYPEMAP: <<HERE
... your typemap code here ...
HERE где ключевое слово TYPEMAP должно появляться в первой колонке новой строки.
Для получения подробностей о написании typemaps см. perlxstypemap.
Инициализация параметров функции
Параметры C-функции обычно инициализируются своими значениями из стека аргументов (который, в свою очередь, содержит параметры, переданные в XSUB из Perl). Typemaps содержат фрагменты кода, используемые для преобразования значений Perl в параметры C. Однако программист может переопределить typemaps и предоставить альтернативный (или дополнительный) код инициализации. Код инициализации начинается с первого =, ; или + в строке в разделе INPUT:. Единственное исключение происходит, если это ; завершает строку, тогда это ; игнорируется.
Следующий код демонстрирует, как предоставить код инициализации для параметров функции. Код инициализации интерпретируется в двойных кавычках компилятором перед добавлением в вывод, поэтому все, что должно интерпретироваться буквально [в основном $, @ или \\], должно быть защищено обратными слешами. Переменные $var, $arg и $type можно использовать как и в typemaps.
bool_t
rpcb_gettime(host,timep)
char *host = (char *)SvPV_nolen($arg);
time_t &timep = 0;
OUTPUT:
timep Это не следует использовать для задания значений по умолчанию для параметров. Обычно это используется, когда параметр функции должен быть обработан другой функцией библиотеки перед использованием. Параметры по умолчанию рассматриваются в следующем разделе.
Если инициализация начинается с =, то она выводится в объявлении переменной ввода, заменяя инициализацию, предоставленную typemap. Если инициализация начинается с ; или +, то она выполняется после объявления всех переменных ввода. В случае ; обычно предоставляемая typemap инициализация не выполняется. В случае + объявление переменной будет включать инициализацию из typemap. Глобальная переменная %v доступна для действительно редкого случая, когда информация из одной инициализации требуется в другой инициализации.
Вот действительно запутанный пример:
bool_t
rpcb_gettime(host,timep)
time_t &timep; /* \$v{timep}=@{[$v{timep}=$arg]} */
char *host + SvOK($v{timep}) ? SvPV_nolen($arg) : NULL;
OUTPUT:
timep Конструкции \$v{timep}=@{[$v{timep}=$arg]} в приведенном выше примере имеют двойную цель: во-первых, когда эта строка обрабатывается xsubpp, Perl-фрагмент $v{timep}=$arg оценивается. Во-вторых, текст оцененного фрагмента выводится в сгенерированный C-файл (внутри C-комментария)! Во время обработки строки char *host, $arg будет оцениваться как ST(0), и $v{timep} будет оцениваться как ST(1).
Значения параметров по умолчанию
Значения параметров XSUB по умолчанию можно указать, поместив оператор присваивания в список параметров. Значение по умолчанию может быть числом, строкой или специальной строкой NO_INIT. Значения по умолчанию всегда должны использоваться только для правых параметров.
Чтобы разрешить XSUB для rpcb_gettime() иметь значение хоста по умолчанию, параметры XSUB можно переупорядочить. XSUB затем вызовет реальную функцию rpcb_gettime() с параметрами в правильном порядке. XSUB можно вызвать из Perl любым из следующих утверждений:
$status = rpcb_gettime( $timep, $host );
$status = rpcb_gettime( $timep ); XSUB будет выглядеть как код, который следует. Используется блок CODE: для вызова реальной функции rpcb_gettime() с параметрами в правильном порядке для этой функции.
bool_t
rpcb_gettime(timep,host="localhost")
char *host
time_t timep = NO_INIT
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL Ключевое слово PREINIT:
Ключевое слово PREINIT: позволяет объявлять дополнительные переменные непосредственно перед или после объявления параметров из раздела INPUT:.
Если переменная объявлена внутри раздела CODE:, она будет следовать за любым кодом typemap, который выводится для входных параметров. Это может привести к тому, что объявление окажется после C-кода, что является синтаксической ошибкой C. Аналогичные ошибки могут произойти, если используется явная инициализация параметров типа ; или + (см. "Инициализация параметров функции"). Объявление этих переменных в разделе INIT: не поможет.
В таких случаях, чтобы принудительно объявить дополнительную переменную вместе с объявлениями других переменных, поместите объявление в раздел PREINIT:. Ключевое слово PREINIT: может быть использовано один или несколько раз в XSUB.
Следующие примеры эквивалентны, но если код использует сложные typemaps, то первый пример безопаснее.
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
PREINIT:
char *host = "localhost";
CODE:
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL В этом конкретном случае ключевое слово INIT: сгенерировало бы тот же C-код, что и ключевое слово PREINIT:. Еще один правильный, но подверженный ошибкам пример:
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
CODE:
char *host = "localhost";
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL Другой способ объявить host - использовать C-блок в разделе CODE:.
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
CODE:
{
char *host = "localhost";
RETVAL = rpcb_gettime( host, &timep );
}
OUTPUT:
timep
RETVAL Возможность размещать дополнительные объявления перед обработкой записей typemap очень полезна в тех случаях, когда преобразования typemap изменяют некоторое глобальное состояние:
MyObject
mutate(o)
PREINIT:
MyState st = global_state;
INPUT:
MyObject o;
CLEANUP:
reset_to(global_state, st); Здесь мы предполагаем, что преобразование в MyObject в разделе INPUT: и из MyObject при обработке RETVAL будет изменять глобальную переменную global_state. После выполнения этих преобразований мы восстанавливаем старое значение global_state (например, для предотвращения утечек памяти).
Есть еще один способ обменять ясность на компактность: разделы INPUT позволяют объявлять C-переменные, которые не появляются в списке параметров подпрограммы. Таким образом, приведенный выше код для mutate() можно переписать как
MyObject
mutate(o)
MyState st = global_state;
MyObject o;
CLEANUP:
reset_to(global_state, st); а код для rpcb_gettime() можно переписать как
bool_t
rpcb_gettime(timep)
time_t timep = NO_INIT
char *host = "localhost";
C_ARGS:
host, &timep
OUTPUT:
timep
RETVAL Ключевое слово SCOPE:
Ключевое слово SCOPE: позволяет включить области действия для конкретного XSUB. Если включено, XSUB будет автоматически вызывать ENTER и LEAVE.
Для поддержки потенциально сложных typemaps, если запись typemap, используемая XSUB, содержит комментарий, подобный /*scope*/, то область действия будет автоматически включена для этого XSUB.
Для включения области действия:
SCOPE: ENABLE Для отключения области действия:
SCOPE: DISABLE Ключевое слово INPUT:
Параметры XSUB обычно вычисляются сразу после входа в XSUB. Ключевое слово INPUT: может использоваться для принудительного вычисления этих параметров немного позже. Ключевое слово INPUT: можно использовать несколько раз в XSUB и можно использовать для перечисления одной или нескольких переменных ввода. Это ключевое слово используется с ключевым словом PREINIT:.
Следующий пример показывает, как входной параметр timep может быть вычислен позже, после PREINIT.
bool_t
rpcb_gettime(host,timep)
char *host
PREINIT:
time_t tt;
INPUT:
time_t timep
CODE:
RETVAL = rpcb_gettime( host, &tt );
timep = tt;
OUTPUT:
timep
RETVAL Следующий пример показывает, что каждый входной параметр вычисляется позже.
bool_t
rpcb_gettime(host,timep)
PREINIT:
time_t tt;
INPUT:
char *host
PREINIT:
char *h;
INPUT:
time_t timep
CODE:
h = host;
RETVAL = rpcb_gettime( h, &tt );
timep = tt;
OUTPUT:
timep
RETVAL Поскольку разделы INPUT позволяют объявлять C-переменные, которые не появляются в списке параметров подпрограммы, это может быть сокращено до:
bool_t
rpcb_gettime(host,timep)
time_t tt;
char *host;
char *h = host;
time_t timep;
CODE:
RETVAL = rpcb_gettime( h, &tt );
timep = tt;
OUTPUT:
timep
RETVAL (Мы использовали наши знания о том, что преобразование ввода для char * является «простым», поэтому host инициализируется в строке объявления, и наше присваивание h = host не выполняется слишком рано. В противном случае, нам нужно было бы иметь присваивание h = host в разделе CODE: или INIT:.)
Ключевые слова IN/OUTLIST/IN_OUTLIST/OUT/IN_OUT
В списке параметров для XSUB можно использовать ключевые слова IN/OUTLIST/IN_OUTLIST/OUT/IN_OUT перед именами параметров. Ключевое слово IN является стандартным, другие ключевые слова указывают, как Perl-интерфейс должен отличаться от C-интерфейса.
Параметры, предваряемые ключевыми словами OUTLIST/IN_OUTLIST/OUT/IN_OUT, считаются используемыми подпрограммой C через указатели. Ключевые слова OUTLIST/OUT указывают, что подпрограмма C не проверяет память, на которую указывает этот параметр, но будет записывать значения через этот указатель, чтобы предоставить дополнительные возвращаемые значения.
Параметры, предваряемые ключевым словом OUTLIST, не отображаются в сигнатуре вызываемой Perl-функции.
Параметры, предваряемые ключевыми словами IN_OUTLIST/IN_OUT/OUT, отображаются как параметры для Perl-функции. За исключением параметров OUT, эти параметры преобразуются в соответствующий C-тип, а затем указатели на эти данные передаются в качестве аргументов функции C. Ожидается, что функция C запишет значения через эти указатели.
Список возвращаемых значений сгенерированной Perl-функции состоит из возвращаемого значения функции C (если XSUB не имеет типа возврата void или не использовалось The NO_OUTPUT Keyword) и всех параметров OUTLIST и IN_OUTLIST (в порядке их появления). При возврате из XSUB параметр Perl IN_OUT/OUT будет изменён, получив значения, записанные функцией C.
Например, XSUB
void
day_month(OUTLIST day, IN unix_time, OUTLIST month)
int day
int unix_time
int month должен использоваться из Perl как
my ($day, $month) = day_month(time); C-сигнатура соответствующей функции должна быть
void day_month(int *day, int unix_time, int *month); Ключевые слова IN/OUTLIST/IN_OUTLIST/IN_OUT/OUT могут быть смешаны с объявлениями в стиле ANSI, как в
void
day_month(OUTLIST int day, int unix_time, OUTLIST int month) (здесь необязательное ключевое слово IN опущено).
Параметры IN_OUT идентичны параметрам, введённым с помощью "Оператора &" и помещенные в раздел OUTPUT: (см. "Ключевое слово OUTPUT:"). Параметры IN_OUTLIST очень похожи, единственное различие заключается в том, что значение, которое функция C записывает через указатель, не изменяет параметр Perl, а добавляется в список вывода.
Параметры OUTLIST/OUT отличаются от параметров IN_OUTLIST/IN_OUT только тем, что начальное значение параметра Perl не читается (и не передаётся функции C - которая получает какой-то мусор вместо этого). Например, та же функция C, что и выше, может быть использована как
void day_month(OUT int day, int unix_time, OUT int month); или
void
day_month(day, unix_time, month)
int &day = NO_INIT
int unix_time
int &month = NO_INIT
OUTPUT:
day
month Однако, сгенерированная Perl-функция вызывается в очень C-подобном стиле:
my ($day, $month);
day_month($day, time, $month);
Ключевое слово length(NAME)
Если одним из входных аргументов функции C является длина строкового аргумента NAME, можно заменить имя аргумента длины на length(NAME) в объявлении XSUB. Этот аргумент должен быть опущен при вызове сгенерированной Perl-функции. Например,
void
dump_chars(char *s, short l)
{
short n = 0;
while (n < l) {
printf("s[%d] = \"\\%#03o\"\n", n, (int)s[n]);
n++;
}
}
MODULE = x PACKAGE = x
void dump_chars(char *s, short length(s)) должно вызываться как dump_chars($string).
Эта директива поддерживается только с объявлениями функций в стиле ANSI.
Переменные списки параметров
XSUB может иметь переменные списки параметров, указав многоточие (...) в списке параметров. Это использование многоточия аналогично тому, что используется в ANSI C. Программист может определить количество аргументов, переданных в XSUB, посмотрев на переменную items, которую компилятор xsubpp предоставляет для всех XSUB. Используя этот механизм, можно создать XSUB, который принимает список параметров неизвестной длины.
Параметр host для XSUB rpcb_gettime() может быть необязательным, поэтому многоточие может использоваться для указания того, что XSUB будет принимать переменное количество параметров. Perl должен иметь возможность вызвать этот XSUB с любым из следующих утверждений.
$status = rpcb_gettime( $timep, $host );
$status = rpcb_gettime( $timep ); Код XS с многоточием следует ниже.
bool_t
rpcb_gettime(timep, ...)
time_t timep = NO_INIT
PREINIT:
char *host = "localhost";
CODE:
if( items > 1 )
host = (char *)SvPV_nolen(ST(1));
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL Ключевое слово C_ARGS:
Ключевое слово C_ARGS: позволяет создавать XSUB, у которых порядок вызова из Perl отличается от вызова из C, без необходимости писать секции CODE: или PPCODE:. Содержимое абзаца C_ARGS: передается в вызываемую C-функцию без изменений.
Например, предположим, что функция C объявлена как
symbolic nth_derivative(int n, symbolic function, int flags); и что стандартные флаги хранятся в глобальной C-переменной default_flags. Предположим, что вы хотите создать интерфейс, который вызывается как
$second_deriv = $function->nth_derivative(2); Для этого объявите XSUB как
symbolic
nth_derivative(function, n)
symbolic function
int n
C_ARGS:
n, function, default_flags Ключевое слово PPCODE:
Ключевое слово PPCODE: является альтернативной формой ключевого слова CODE: и используется, чтобы сказать компилятору xsubpp, что программист предоставляет код для управления стеком аргументов для возвращаемых значений XSUB. Иногда требуется, чтобы XSUB возвращал список значений, а не одно значение. В этих случаях необходимо использовать PPCODE: и явно поместить список значений в стек.
Разница между секциями PPCODE: и CODE: заключается в инициализации макроса SP (который представляет текущий указатель стека Perl), и в обработке данных в стеке при возвращении из XSUB. В секциях CODE: SP сохраняет значение, которое было при входе в XSUB: SP находится на указателе функции (который следует за последним параметром). В секциях PPCODE: SP перемещается назад к началу списка параметров, что позволяет макросам PUSH*() помещать выходные значения в позицию, которую Perl ожидает при возвращении XSUB в Perl.
Сгенерированный трейлер для секции CODE: гарантирует, что количество возвращаемых значений, которые увидит Perl, равно либо 0, либо 1 (в зависимости от типа возвращаемого значения функции C, и эвристики, упомянутой в "Переменной RETVAL"). Трейлер, сгенерированный для секции PPCODE:, основан на количестве возвращаемых значений и количестве раз, когда SP был обновлён макросами [X]PUSH*().
Обратите внимание, что макросы ST(i), XST_m*() и XSRETURN*() работают одинаково хорошо в секциях CODE: и PPCODE:.
Следующий XSUB вызовет функцию C rpcb_gettime() и вернёт её два выходных значения, timep и status, в Perl как один список.
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
bool_t status;
PPCODE:
status = rpcb_gettime( host, &timep );
EXTEND(SP, 2);
PUSHs(sv_2mortal(newSViv(status)));
PUSHs(sv_2mortal(newSViv(timep))); Обратите внимание, что программист должен предоставить код C, необходимый для вызова реальной функции rpcb_gettime() и для правильного размещения возвращаемых значений в стеке аргументов.
Тип возврата void для этой функции сообщает компилятору xsubpp, что переменная RETVAL не нужна или используется, и что она не должна создаваться. В большинстве случаев тип возврата void следует использовать с директивой PPCODE:.
Макрос EXTEND() используется для выделения места в стеке аргументов для 2 возвращаемых значений. Директива PPCODE: заставляет компилятор xsubpp создать указатель стека, доступный как SP, и именно этот указатель используется в макросе EXTEND(). Значения затем помещаются в стек с помощью макроса PUSHs().
Теперь функцию rpcb_gettime() можно использовать из Perl следующим утверждением.
($status, $timep) = rpcb_gettime("localhost"); При обработке выходных параметров с секцией PPCODE, убедитесь, что правильно обрабатывается магия 'set'. Подробности о магии 'set' см. в perlguts.
Возвращение Undef и пустых списков
Иногда программист хочет просто вернуть undef или пустой список, если функция завершается неудачно, а не отдельное значение состояния. Функция rpcb_gettime() предлагает именно такую ситуацию. Если функция завершается успешно, мы хотели бы получить время, а если завершается неудачно, мы хотели бы получить undef. В следующем Perl-коде значение $timep будет либо undef, либо допустимым временем.
$timep = rpcb_gettime( "localhost" ); Следующий XSUB использует тип возврата SV * только как мнемоническое обозначение и использует блок CODE: для указания компилятору, что программист предоставил весь необходимый код. Вызов sv_newmortal() инициализирует возвращаемое значение undef, делая его значением по умолчанию.
SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
ST(0) = sv_newmortal();
if( rpcb_gettime( host, &timep ) )
sv_setnv( ST(0), (double)timep); Следующий пример демонстрирует, как можно явно поместить undef в возвращаемое значение, если это необходимо.
SV *
rpcb_gettime(host)
char * host
PREINIT:
time_t timep;
bool_t x;
CODE:
if( rpcb_gettime( host, &timep ) ){
ST(0) = sv_newmortal();
sv_setnv( ST(0), (double)timep);
}
else{
ST(0) = &PL_sv_undef;
} Чтобы вернуть пустой список, необходимо использовать блок PPCODE: и не помещать возвращаемые значения в стек.
void
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
PPCODE:
if( rpcb_gettime( host, &timep ) )
PUSHs(sv_2mortal(newSViv(timep)));
else{
/* Nothing pushed on stack, so an empty
* list is implicitly returned. */
} Некоторые люди могут захотеть включить явное return в вышеприведённом XSUB, вместо того, чтобы позволить выполнению перейти к концу. В таких ситуациях следует использовать XSRETURN_EMPTY вместо этого. Это гарантирует правильную корректировку стека XSUB. См. perlapi для других макросов XSRETURN.
Поскольку макросы XSRETURN_* могут использоваться и с блоками CODE, можно переписать этот пример следующим образом:
int
rpcb_gettime(host)
char *host
PREINIT:
time_t timep;
CODE:
RETVAL = rpcb_gettime( host, &timep );
if (RETVAL == 0)
XSRETURN_UNDEF;
OUTPUT:
RETVAL Фактически, это условие можно поместить и в секцию POSTCALL:. Вместе с упрощениями PREINIT: это приводит к:
int
rpcb_gettime(host)
char *host
time_t timep;
POSTCALL:
if (RETVAL == 0)
XSRETURN_UNDEF; Ключевое слово REQUIRE:
Ключевое слово REQUIRE: используется для указания минимальной версии компилятора xsubpp, необходимой для компиляции модуля XS. Модуль XS, содержащий следующее утверждение, будет компилироваться только с версией xsubpp 1.922 или выше:
REQUIRE: 1.922 Ключевое слово CLEANUP:
Это ключевое слово может использоваться, когда XSUB требует специальных процедур очистки перед завершением. Когда используется ключевое слово CLEANUP:, оно должно следовать за любыми блоками CODE: или OUTPUT:, которые присутствуют в XSUB. Код, указанный для блока очистки, будет добавлен в качестве последних операторов в XSUB.
Ключевое слово POSTCALL:
Это ключевое слово может использоваться, когда XSUB требует специальных процедур, выполняемых после выполнения вызова C-подпрограммы. При использовании ключевого слова POSTCALL:, оно должно предшествовать блокам OUTPUT: и CLEANUP:, если они присутствуют в XSUB.
Примеры см. в "Ключевом слове NO_OUTPUT" и "Возвращении Undef и пустых списков".
Блок POSTCALL: не имеет большого смысла, когда вызов C-подпрограммы обеспечивается пользователем с помощью секций CODE: или PPCODE:.
Ключевое слово BOOT:
Ключевое слово BOOT: используется для добавления кода в функцию загрузки расширения. Функция загрузки генерируется компилятором xsubpp и обычно содержит операторы, необходимые для регистрации всех XSUB с Perl. С помощью ключевого слова BOOT: программист может указать компилятору добавить дополнительные операторы в функцию загрузки.
Это ключевое слово может быть использовано в любое время после первого ключевого слова MODULE и должно появляться на отдельной строке. Первая пустая строка после ключевого слова завершит блок кода.
BOOT:
# The following message will be printed when the
# bootstrap function executes.
printf("Hello from the bootstrap!\n"); Ключевое слово VERSIONCHECK:
Ключевое слово VERSIONCHECK: соответствует опциям xsubpp -versioncheck и -noversioncheck. Это ключевое слово переопределяет опции командной строки. Проверка версий включена по умолчанию. При включенной проверке версий модуль XS попытается проверить, что его версия соответствует версии модуля PM.
Для включения проверки версий:
VERSIONCHECK: ENABLE Для отключения проверки версий:
VERSIONCHECK: DISABLE Обратите внимание, что если версия модуля PM является значением NV (вещественным числом с плавающей точкой), оно будет преобразовано в строку с возможной потерей точности (в настоящее время отбрасывается девять десятичных знаков), поэтому может не соответствовать версии модуля XS. Рекомендуется привести объявление $VERSION к строковому представлению, если используются длинные номера версий.
Ключевое слово PROTOTYPES:
Ключевое слово PROTOTYPES: соответствует опциям xsubpp -prototypes и -noprototypes. Это ключевое слово переопределяет опции командной строки. Прототипы отключены по умолчанию. Если прототипы включены, XSUB будут предоставлены прототипы Perl. Это ключевое слово может быть использовано несколько раз в модуле XS для включения и отключения прототипов для различных частей модуля. Обратите внимание, что xsubpp будет напоминать вам, если вы не явно включите или отключите прототипы, выведя:
Please specify prototyping behavior for Foo.xs (see perlxs manual) Для включения прототипов:
PROTOTYPES: ENABLE Для отключения прототипов:
PROTOTYPES: DISABLE Ключевое слово PROTOTYPE:
Это ключевое слово аналогично ключевому слову PROTOTYPES: выше, но может быть использовано для принудительного использования определенного прототипа для XSUB. Это ключевое слово переопределяет все другие опции и ключевые слова прототипа, но влияет только на текущий XSUB. См. "Прототипы" в perlsub для информации о прототипах Perl.
bool_t
rpcb_gettime(timep, ...)
time_t timep = NO_INIT
PROTOTYPE: $;$
PREINIT:
char *host = "localhost";
CODE:
if( items > 1 )
host = (char *)SvPV_nolen(ST(1));
RETVAL = rpcb_gettime( host, &timep );
OUTPUT:
timep
RETVAL Если прототипы включены, вы можете отключить их локально для данного XSUB, как показано в следующем примере:
void
rpcb_gettime_noproto()
PROTOTYPE: DISABLE
... Ключевое слово ALIAS:
Ключевое слово ALIAS: позволяет XSUB иметь два или более уникальных имени Perl и знать, какое из этих имен было использовано при вызове. Имена Perl могут быть полностью квалифицированы с именами пакетов. Каждому псевдониму присваивается индекс. Компилятор настроит переменную ix, содержащую индекс псевдонима, который был использован. При вызове XSUB с его объявленным именем ix будет равно 0.
Следующий пример создаст псевдонимы FOO::gettime() и BAR::getit() для этой функции.
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
ALIAS:
FOO::gettime = 1
BAR::getit = 2
INIT:
printf("# ix = %d\n", ix );
OUTPUT:
timep Ключевое слово OVERLOAD:
Вместо написания перегруженного интерфейса с помощью чистого Perl, вы также можете использовать ключевое слово OVERLOAD для определения дополнительных имен Perl для ваших функций (как ключевое слово ALIAS: выше). Однако перегруженные функции должны быть определены таким образом, чтобы принимать количество параметров, предоставляемое системой перегрузки Perl. Для большинства методов перегрузки это будет три параметра; для функции nomethod это будет четыре. Однако битовые операторы &, |, ^ и ~ могут вызываться с тремя или пятью аргументами (см. overload).
Если какая-либо функция имеет ключевое слово OVERLOAD:, в сгенерированном файле c компилятором xsubpp будут определены несколько дополнительных строк для регистрации с магией перегрузки.
Поскольку освященные объекты фактически хранятся как RV, полезно использовать возможности typemap для предварительной обработки параметров и извлечения фактического SV, хранящегося в освященном RV. См. пример T_PTROBJ_SPECIAL ниже.
Для использования ключевого слова OVERLOAD: создайте функцию XS, которая принимает три входных параметра (или используйте определение C-стиля '...') следующим образом:
SV *
cmp (lobj, robj, swap)
My_Module_obj lobj
My_Module_obj robj
IV swap
OVERLOAD: cmp <=>
{ /* function defined here */} В этом случае функция перегрузит оба оператора сравнения трех типов. Для всех операций перегрузки, использующих не буквенные символы, вы должны ввести параметр без кавычек, разделяя несколько перегрузок пробелами. Обратите внимание, что "" (перегрузка строкового преобразования) следует вводить как \"\" (т. е. с экранированием).
Поскольку, как упоминалось выше, битовые операторы могут принимать дополнительные аргументы, вы можете использовать что-то вроде (lobj, robj, swap, ...) (с литералом ...) в качестве списка параметров.
Ключевое слово FALLBACK:
В дополнение к ключевому слову OVERLOAD, если вам нужно контролировать, как Perl автоматически генерирует отсутствующие перегруженные операторы, вы можете установить ключевое слово FALLBACK в заголовке модуля, как это:
MODULE = RPC PACKAGE = RPC
FALLBACK: TRUE
... где FALLBACK может принимать любое из трех значений TRUE, FALSE или UNDEF. Если вы не устанавливаете значение FALLBACK при использовании OVERLOAD, оно по умолчанию равно UNDEF. FALLBACK не используется, кроме как когда определены одна или несколько функций, использующих OVERLOAD. Подробнее см. "fallback" в overload.
Ключевое слово INTERFACE:
Это ключевое слово объявляет текущий XSUB как хранителя заданной подписи вызова. Если за этим ключевым словом следует какой-либо текст, он считается списком функций, имеющих эту подпись, и должен быть присоединен к текущему XSUB.
Например, если у вас есть 4 функции C multiply(), divide(), add(), subtract() со следующей подписью:
symbolic f(symbolic, symbolic); вы можете заставить их все использовать один XSUB с помощью этого:
symbolic
interface_s_ss(arg1, arg2)
symbolic arg1
symbolic arg2
INTERFACE:
multiply divide
add subtract (Это весь код XSUB для 4 функций Perl!) Четыре сгенерированные функции Perl используют имена соответствующих функций C.
Преимущество этого подхода по сравнению с ключевым словом ALIAS: заключается в том, что нет необходимости кодировать оператор switch, каждая функция Perl (которая использует тот же XSUB) знает, какую функцию C ей нужно вызвать. Кроме того, можно добавить дополнительную функцию remainder() во время выполнения, используя
CV *mycv = newXSproto("Symbolic::remainder",
XS_Symbolic_interface_s_ss, __FILE__, "$$");
XSINTERFACE_FUNC_SET(mycv, remainder); скажем, из другого XSUB. (Этот пример предполагает, что не было раздела INTERFACE_MACRO:, в противном случае необходимо использовать что-то другое вместо XSINTERFACE_FUNC_SET, см. следующий раздел.)
Ключевое слово INTERFACE_MACRO:
Это ключевое слово позволяет определить INTERFACE другим способом извлечения указателя на функцию из XSUB. Текст, который следует за этим ключевым словом, должен содержать имена макросов, которые будут извлекать/устанавливать указатель на функцию. Макрос извлечения получает тип возвращаемого значения, CV* и XSANY.any_dptr для этого CV*. Макрос установки получает cv и указатель на функцию.
Значение по умолчанию - XSINTERFACE_FUNC и XSINTERFACE_FUNC_SET. Ключевое слово INTERFACE со списком пустых функций может быть опущено, если используется ключевое слово INTERFACE_MACRO.
Предположим, что в предыдущем примере указатели на функции multiply(), divide(), add(), subtract() хранятся в глобальном массиве C fp[] со смещениями multiply_off, divide_off, add_off, subtract_off. Тогда можно использовать
#define XSINTERFACE_FUNC_BYOFFSET(ret,cv,f) \
((XSINTERFACE_CVT_ANON(ret))fp[CvXSUBANY(cv).any_i32])
#define XSINTERFACE_FUNC_BYOFFSET_set(cv,f) \
CvXSUBANY(cv).any_i32 = CAT2( f, _off ) в разделе C,
symbolic
interface_s_ss(arg1, arg2)
symbolic arg1
symbolic arg2
INTERFACE_MACRO:
XSINTERFACE_FUNC_BYOFFSET
XSINTERFACE_FUNC_BYOFFSET_set
INTERFACE:
multiply divide
add subtract в разделе XSUB.
Ключевое слово INCLUDE:
Это ключевое слово можно использовать для включения других файлов в модуль XS. Другие файлы могут содержать код XS. INCLUDE: также может использоваться для выполнения команды по генерации кода XS, который будет включен в модуль.
Файл Rpcb1.xsh содержит нашу rpcb_gettime() функцию:
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep Модуль XS может использовать INCLUDE: для включения этого файла в него.
INCLUDE: Rpcb1.xsh Если параметрам ключевого слова INCLUDE: следует символ | (|), то компилятор интерпретирует параметры как команду. Эта функция устаревает в пользу директивы INCLUDE_COMMAND:, как описано ниже.
INCLUDE: cat Rpcb1.xsh | Не используйте это для запуска perl: INCLUDE: perl | запустит perl, который первым встретится в вашей переменной среды PATH, а не обязательно тот же perl, который используется для запуска xsubpp. См. "Ключевое слово INCLUDE_COMMAND:".
Ключевое слово INCLUDE_COMMAND:
Выполняет указанную команду и включает ее вывод в текущий документ XS. INCLUDE_COMMAND присваивает специальное значение маркерам $^X, так как он запускает тот же интерпретатор perl, который запускает xsubpp:
INCLUDE_COMMAND: cat Rpcb1.xsh
INCLUDE_COMMAND: $^X -e ... Ключевое слово CASE:
Ключевое слово CASE: позволяет XSUB иметь несколько различных частей, причем каждая часть действует как виртуальный XSUB. CASE: жадный, и если он используется, все другие ключевые слова XS должны быть заключены в CASE:. Это означает, что ничего не может предшествовать первому CASE: в XSUB, и все, что следует за последним CASE:, включено в этот случай.
CASE: может переключаться через параметр XSUB, через переменную ix ALIAS: (см. "Ключевое слово ALIAS:"), или, возможно, через переменную items (см. "Списки параметров переменной длины"). Последний CASE: становится по умолчанию, если он не связан с условным выражением. Следующий пример демонстрирует CASE, переключаемый через ix с функцией rpcb_gettime() имеющей псевдоним x_gettime(). Когда функция вызывается как rpcb_gettime(), ее параметрами являются обычные (char *host, time_t *timep), но когда функция вызывается как x_gettime(), ее параметры инвертируются, (time_t *timep, char *host).
long
rpcb_gettime(a,b)
CASE: ix == 1
ALIAS:
x_gettime = 1
INPUT:
# 'a' is timep, 'b' is host
char *b
time_t a = NO_INIT
CODE:
RETVAL = rpcb_gettime( b, &a );
OUTPUT:
a
RETVAL
CASE:
# 'a' is host, 'b' is timep
char *a
time_t &b = NO_INIT
OUTPUT:
b
RETVAL Эту функцию можно вызвать любым из следующих утверждений. Обратите внимание на разные списки аргументов.
$status = rpcb_gettime( $host, $timep );
$status = x_gettime( $timep, $host ); Ключевое слово EXPORT_XSUB_SYMBOLS:
Ключевое слово EXPORT_XSUB_SYMBOLS: — это то, что вам, вероятно, никогда не понадобится. В версиях perl, более ранних чем 5.16.0, это ключевое слово ничего не делает. Начиная с 5.16, символы XSUB больше не экспортируются по умолчанию. То есть, они являются static функциями. Если вы включите
EXPORT_XSUB_SYMBOLS: ENABLE в ваш код XS, XSUB, следующие за этой строкой, не будут объявляться static. Вы можете отключить это позже с помощью
EXPORT_XSUB_SYMBOLS: DISABLE что, опять же, является значением по умолчанию, которое, вероятно, никогда не нужно изменять. Вы не можете использовать это ключевое слово в версиях perl до 5.16, чтобы сделать XSUB static.
Унарный оператор &
Унарный оператор & в разделе INPUT: используется для того, чтобы указать xsubpp, что он должен преобразовать значение Perl в/из C, используя тип C слева от &, но предоставить указатель на это значение, когда вызывается функция C.
Это полезно для избежания блока CODE: для C-функции, которая принимает параметр по ссылке. Обычно параметр не должен быть типом указателя (int или long, но не int* или long*).
Следующий XSUB сгенерирует неправильный C-код. Компилятор xsubpp преобразует его в код, который вызывает rpcb_gettime() с параметрами (char *host, time_t timep), но реальный rpcb_gettime() хочет, чтобы параметр timep был типа time_t*, а не time_t.
bool_t
rpcb_gettime(host,timep)
char *host
time_t timep
OUTPUT:
timep Эта проблема исправляется с помощью оператора &. Компилятор xsubpp теперь преобразует это в код, который правильно вызывает rpcb_gettime() с параметрами (char *host, time_t *timep). Он делает это, передавая &, поэтому вызов функции выглядит как rpcb_gettime(host, &timep).
bool_t
rpcb_gettime(host,timep)
char *host
time_t &timep
OUTPUT:
timep Вставка POD, комментариев и директив препроцессора C
Директивы препроцессора C разрешены внутри блоков BOOT:, PREINIT:, INIT:, CODE:, PPCODE:, POSTCALL: и CLEANUP:, а также вне функций. Комментарии разрешены где угодно после ключевого слова MODULE. Компилятор пропустит директивы препроцессора без изменений и удалит строки с комментариями. Документация POD разрешена в любой точке, как в разделах C, так и XS. POD должен быть завершен командой =cut; xsubpp завершит работу с ошибкой, если этого не произойдет. Вероятность того, что сгенерированный человеком C-код будет ошибочно принят за POD, очень низка, так как большинство стилей форматирования с отступами оставляют пробелы перед любой строкой, начинающейся с =. Сгенерированные машиной файлы XS могут попасть в эту ловушку, если не позаботиться о том, чтобы пробел разрывал последовательность "\n=".
Комментарии могут быть добавлены к XSUB с помощью # в качестве первой не содержащей пробелов части строки. Следует быть осторожным, чтобы не заставить комментарий выглядеть как директива препроцессора C, иначе он будет интерпретирован как таковая. Самый простой способ предотвратить это — добавить пробелы перед #.
Если вы используете директивы препроцессора для выбора одной из двух версий функции, используйте
#if ... version1
#else /* ... version2 */
#endif а не
#if ... version1
#endif
#if ... version2
#endif потому что в противном случае xsubpp посчитает, что вы сделали дублированное определение функции. Также поставьте пустую строку перед #else/#endif, чтобы она не считалась частью тела функции.
Использование XS с C++
Если имя XSUB содержит ::, оно считается C++ методом. Сгенерированная Perl-функция будет предполагать, что ее первый аргумент — указатель на объект. Указатель на объект будет храниться в переменной с именем THIS. Объект должен быть создан с помощью функции new() в C++ и должен быть благословлен Perl макросом sv_setref_pv(). Благословение объекта Perl может быть обработано с помощью typemap. Пример typemap показан в конце этого раздела.
Если возвращаемый тип XSUB включает static, метод считается статическим методом. Он будет вызывать C++ функцию с помощью синтаксиса class::method(). Если метод не является статическим, функция будет вызвана с помощью синтаксиса THIS->method().
Следующие примеры будут использовать следующий C++ класс.
class color {
public:
color();
~color();
int blue();
void set_blue( int );
private:
int c_blue;
}; XSUB для методов blue() и set_blue() определены с именем класса, но параметр для объекта (THIS или «self») неявный и не указан.
int
color::blue()
void
color::set_blue( val )
int val Обе Perl-функции будут ожидать объект в качестве первого параметра. В сгенерированном C++ коде объект называется THIS, и вызов метода будет выполнен над этим объектом. Итак, в C++ коде методы blue() и set_blue() будут вызваны следующим образом:
RETVAL = THIS->blue();
THIS->set_blue( val ); Вы также можете написать единый метод get/set с необязательным аргументом:
int
color::blue( val = NO_INIT )
int val
PROTOTYPE $;$
CODE:
if (items > 1)
THIS->set_blue( val );
RETVAL = THIS->blue();
OUTPUT:
RETVAL Если имя функции — DESTROY, то будет вызвана C++ функция delete, а THIS будет передана в качестве параметра. Сгенерированный C++ код для
void
color::DESTROY() будет выглядеть так:
color *THIS = ...; // Initialized as in typemap
delete THIS; Если имя функции — new, то будет вызвана C++ функция new для создания динамического C++ объекта. XSUB будет ожидать имя класса, которое будет храниться в переменной CLASS, в качестве первого аргумента.
color *
color::new() Сгенерированный C++ код вызовет new.
RETVAL = new color(); Ниже приведен пример typemap, который можно использовать для этого примера C++.
TYPEMAP
color * O_OBJECT
OUTPUT
# The Perl object is blessed into 'CLASS', which should be a
# char* having the name of the package for the blessing.
O_OBJECT
sv_setref_pv( $arg, CLASS, (void*)$var );
INPUT
O_OBJECT
if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) )
$var = ($type)SvIV((SV*)SvRV( $arg ));
else{
warn("${Package}::$func_name() -- " .
"$var is not a blessed SV reference");
XSRETURN_UNDEF;
} Стратегия интерфейса
При проектировании интерфейса между Perl и C-библиотекой прямое преобразование из C в XS (например, созданное h2xs -x) зачастую достаточно. Однако иногда интерфейс будет выглядеть очень похожим на C и неинтуитивным, особенно когда C-функция изменяет один из своих параметров или возвращает ошибку непосредственно (как в случае «отрицательные возвращаемые значения означают ошибку»). В случаях, когда программист желает создать более Perl-подобный интерфейс, следующая стратегия может помочь в определении наиболее важных частей интерфейса.
Определите C-функции с параметрами ввода/вывода или параметрами вывода. XSUB для этих функций могут возвращать списки в Perl.
Определите C-функции, которые используют некоторую информацию как указание на ошибку. Они могут быть кандидатами для возврата undef или пустого списка в случае ошибки. Если ошибка может быть обнаружена без вызова C-функции, вы можете использовать раздел INIT: для сообщения об ошибке. Для ошибок, обнаруживаемых после возврата C-функции, вы можете использовать раздел POSTCALL: для обработки ошибки. В более сложных случаях используйте разделы CODE: или PPCODE:.
Если многие функции используют одно и то же указание на ошибку, основанное на возвращаемом значении, вы можете создать специальный typedef для обработки этой ситуации. Разместите
typedef int negative_is_failure; в начале файла XS и создайте запись typemap OUTPUT для negative_is_failure, которая преобразует отрицательные значения в undef, или, возможно, croak(). После этого возвращаемое значение типа negative_is_failure создаст более Perl-подобный интерфейс.
Определите значения, используемые только самими C- и XSUB-функциями, например, когда параметр функции должен быть содержимым глобальной переменной. Если Perl не должен обращаться к содержимому значения, то, возможно, не нужно предоставлять перевод этого значения из C в Perl.
Определите указатели в списках параметров и возвращаемых значениях C-функции. Некоторые указатели могут использоваться для реализации параметров ввода/вывода или параметров вывода; они могут обрабатываться в XS с помощью унарного оператора & и, возможно, с использованием ключевого слова NO_INIT. Другие потребуют обработки типов, таких как int *, и необходимо решить, что полезный перевод Perl должен делать в таком случае. При ясном семантическом значении рекомендуется поместить перевод в файл typemap.
Определите структуры, используемые C-функциями. Во многих случаях может быть полезно использовать typemap T_PTROBJ для этих структур, чтобы ими можно было управлять в Perl как благословлёнными объектами. (Это обрабатывается автоматически h2xs -x).
Если один и тот же C-тип используется в нескольких разных контекстах, которые требуют различных переводов, typedef несколько новых типов, сопоставленных с этим C-типом, и создайте отдельные записи typemap для этих новых типов. Используйте эти типы в объявлениях возвращаемого типа и параметров XSUB.
Perl-объекты и C-структуры
При работе со C-структурами следует выбрать либо T_PTROBJ, либо T_PTRREF для типа XS. Оба типа предназначены для обработки указателей на сложные объекты. Тип T_PTRREF позволит объекту Perl быть необлагословленным, а тип T_PTROBJ требует, чтобы объект был благословлён. Используя T_PTROBJ, можно добиться формы проверки типов, потому что XSUB попытается проверить, является ли Perl-объект ожидаемого типа.
Следующий XS-код показывает функцию getnetconfigent(), которая используется с ONC+ TIRPC. Функция getnetconfigent() вернёт указатель на C-структуру и имеет показанный ниже C-прототип. Пример продемонстрирует, как C-указатель превращается в Perl-ссылку. Perl будет рассматривать эту ссылку как указатель на благословлённый объект и попытается вызвать деструктор для объекта. Деструктор будет предоставлен в XS-источнике для освобождения памяти, используемой getnetconfigent(). Деструкторы в XS могут быть созданы путём указания XSUB-функции, имя которой заканчивается словом DESTROY. XS-деструкторы могут использоваться для освобождения памяти, которая может быть выделена с помощью malloc() другой XSUB.
struct netconfig *getnetconfigent(const char *netid); Для struct netconfig будет создан typedef. Perl-объект будет благословлён в классе, соответствующем имени C-типа, с добавленным тегом Ptr, а имя не должно содержать вложенных пробелов, если это будет имя Perl-пакета. Деструктор будет размещён в классе, соответствующем классу объекта, и ключевое слово PREFIX будет использовано для сокращения имени до слова DESTROY, так как Perl ожидает этого.
typedef struct netconfig Netconfig;
MODULE = RPC PACKAGE = RPC
Netconfig *
getnetconfigent(netid)
char *netid
MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_
void
rpcb_DESTROY(netconf)
Netconfig *netconf
CODE:
printf("Now in NetconfigPtr::DESTROY\n");
free( netconf ); Этот пример требует следующей записи typemap. Обратитесь к perlxstypemap за дополнительной информацией о добавлении новых typemap для расширения.
TYPEMAP
Netconfig * T_PTROBJ Этот пример будет использоваться со следующими Perl-командами.
use RPC;
$netconf = getnetconfigent("udp"); Когда Perl уничтожит объект, на который ссылается $netconf, он отправит объект в предоставленную XSUB-функцию DESTROY. Perl не может определить и не заботится о том, что этот объект — C-структура, а не Perl-объект. В этом смысле нет никакой разницы между объектом, созданным XSUB getnetconfigent(), и объектом, созданным обычной Perl-подпрограммой.
Безопасное хранение статических данных в XS
Начиная с Perl 5.8, для безопасного хранения статических данных в XS-модулях, которые будут использоваться из многопоточного Perl, определён макрос-фреймворк.
Хотя он предназначен в первую очередь для использования с многопоточным Perl, макросы разработаны так, чтобы они работали и с не-многопоточным Perl.
Поэтому настоятельно рекомендуется использовать эти макросы во всех XS-модулях, использующих статические данные.
Самый простой способ получить набор макросов-шаблонов — указать опцию -g (--global) при использовании h2xs (см. h2xs).
Ниже приведён пример модуля, использующего эти макросы.
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/* Global Data */
#define MY_CXT_KEY "BlindMice::_guts" XS_VERSION
typedef struct {
int count;
char name[3][100];
} my_cxt_t;
START_MY_CXT
MODULE = BlindMice PACKAGE = BlindMice
BOOT:
{
MY_CXT_INIT;
MY_CXT.count = 0;
strcpy(MY_CXT.name[0], "None");
strcpy(MY_CXT.name[1], "None");
strcpy(MY_CXT.name[2], "None");
}
int
newMouse(char * name)
PREINIT:
dMY_CXT;
CODE:
if (MY_CXT.count >= 3) {
warn("Already have 3 blind mice");
RETVAL = 0;
}
else {
RETVAL = ++ MY_CXT.count;
strcpy(MY_CXT.name[MY_CXT.count - 1], name);
}
OUTPUT:
RETVAL
char *
get_mouse_name(index)
int index
PREINIT:
dMY_CXT;
CODE:
if (index > MY_CXT.count)
croak("There are only 3 blind mice.");
else
RETVAL = MY_CXT.name[index - 1];
OUTPUT:
RETVAL
void
CLONE(...)
CODE:
MY_CXT_CLONE; Ссылка MY_CXT
- MY_CXT_KEY
-
Этот макрос используется для определения уникального ключа для ссылки на статические данные для модуля XS. Предлагаемая схема именования, используемая h2xs, состоит из имени модуля, строки "::_guts" и номера версии модуля.
#define MY_CXT_KEY "MyModule::_guts" XS_VERSION - typedef my_cxt_t
-
Этот тип структуры обязательно должен называться
my_cxt_t. ДругиеCXT*макросы предполагают существование типаmy_cxt_t.Объявите тип
my_cxt_t, представляющий собой структуру, содержащую все данные, которые должны быть локальными для интерпретатора.typedef struct { int some_value; } my_cxt_t; - START_MY_CXT
-
Всегда размещайте макрос START_MY_CXT непосредственно после объявления
my_cxt_t. - MY_CXT_INIT
-
Макрос MY_CXT_INIT инициализирует хранилище для структуры
my_cxt_t.Он обязательно должен вызываться один раз, обычно в разделе BOOT:. Если вы поддерживаете несколько интерпретаторов, он должен вызываться один раз в каждом экземпляре интерпретатора, за исключением интерпретаторов, клонированных из существующих. (Но см. "MY_CXT_CLONE" ниже.)
- dMY_CXT
-
Используйте макрос dMY_CXT (объявление) во всех функциях, которые обращаются к MY_CXT.
- MY_CXT
-
Используйте макрос MY_CXT для доступа к членам структуры
my_cxt_t. Например, еслиmy_cxt_ttypedef struct { int index; } my_cxt_t;то используйте это для доступа к члену
indexdMY_CXT; MY_CXT.index = 2; - aMY_CXT/pMY_CXT
-
dMY_CXTможет быть довольно дорогостоящим для вычисления, и чтобы избежать накладных расходов на вызов его в каждой функции, возможно, передать объявление в другие функции, используя макросыaMY_CXT/pMY_CXT, напримерvoid sub1() { dMY_CXT; MY_CXT.index = 1; sub2(aMY_CXT); } void sub2(pMY_CXT) { MY_CXT.index = 2; }Аналогично
pTHX, существуют эквивалентные формы для случаев, когда макрос является первым или последним в нескольких аргументах, где подчёркивание представляет запятую, т.е._aMY_CXT,aMY_CXT_,_pMY_CXTиpMY_CXT_. - MY_CXT_CLONE
-
По умолчанию, когда создается новый интерпретатор как копия существующего (например, через
threads->create()), оба интерпретатора используют одну и ту же физическую структуру my_cxt_t. ВызовMY_CXT_CLONE(обычно через функцию пакетаCLONE()) создаёт байтовую копию структуры, и любой последующий dMY_CXT будет обращаться к копии вместо оригинала. - MY_CXT_INIT_INTERP(my_perl)
- dMY_CXT_INTERP(my_perl)
-
Это версии макросов, которые принимают явный интерпретатор в качестве аргумента.
Обратите внимание, что эти макросы будут работать вместе только в одном исходном файле; то есть dMY_CTX в одном исходном файле будет обращаться к другой структуре, чем dMY_CTX в другом исходном файле.
Многопоточные системные интерфейсы
Начиная с Perl 5.8, на уровне C/C++ Perl умеет оборачивать системные/библиотечные интерфейсы, имеющие многопоточные версии (например, getpwent_r()), в макросы-фронтеранды (например, getpwent()), которые корректно обрабатывают взаимодействие в многопоточной среде с интерпретатором Perl. Это происходит прозрачно, всё, что вам нужно сделать, это инициализировать интерпретатор Perl.
Это обертывание происходит всегда при компиляции исходного кода Perl (определен PERL_CORE) или расширений Perl (определен PERL_EXT). При компиляции кода XS вне ядра Perl обертывание не происходит до Perl 5.28. Начиная с этого релиза вы можете
#define PERL_REENTRANT в вашем коде, чтобы включить обертывание. Рекомендуется сделать это, если вы используете такие функции, так как смешивание форм _r (как Perl, скомпилированный для многопоточного выполнения, будет делать) и форм без _r не определено (несогласованные результаты, повреждение данных или даже сбои становятся более вероятными), да и не очень переносимо. К сожалению, не все системы имеют все формы _r, но использование этой формы #define даёт ту защиту, которую Perl знает, доступна на каждой системе.
ПРИМЕРЫ
Файл RPC.xs: Интерфейс к некоторым функциям библиотеки привязки ONC+ RPC.
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <rpc/rpc.h>
typedef struct netconfig Netconfig;
MODULE = RPC PACKAGE = RPC
SV *
rpcb_gettime(host="localhost")
char *host
PREINIT:
time_t timep;
CODE:
ST(0) = sv_newmortal();
if( rpcb_gettime( host, &timep ) )
sv_setnv( ST(0), (double)timep );
Netconfig *
getnetconfigent(netid="udp")
char *netid
MODULE = RPC PACKAGE = NetconfigPtr PREFIX = rpcb_
void
rpcb_DESTROY(netconf)
Netconfig *netconf
CODE:
printf("NetconfigPtr::DESTROY\n");
free( netconf ); Файл typemap: Настройка типов для RPC.xs. (см. perlxstypemap)
TYPEMAP
Netconfig * T_PTROBJ Файл RPC.pm: Модуль Perl для расширения RPC.
package RPC;
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw(rpcb_gettime getnetconfigent);
bootstrap RPC;
1; Файл rpctest.pl: Программа тестов Perl для расширения RPC.
use RPC;
$netconf = getnetconfigent();
$a = rpcb_gettime();
print "time = $a\n";
print "netconf = $netconf\n";
$netconf = getnetconfigent("tcp");
$a = rpcb_gettime("poplar");
print "time = $a\n";
print "netconf = $netconf\n"; ЗАМЕЧАНИЯ
Код XS имеет полный доступ к системным вызовам, включая функции библиотеки C. Таким образом, он имеет возможность вмешиваться в то, что настроило ядро Perl или другие модули, такие как обработчики сигналов или файловые дескрипторы. Он может испортить память или любое количество вредных действий. Не делайте этого.
Некоторые модули имеют цикл обработки событий, ожидая пользовательский ввод. Вероятность того, что два таких модуля будут адекватно работать вместе в одном приложении Perl, очень низка.
В целом, интерпретатор Perl считает себя центром вселенной, что касается программы Perl. Код XS рассматривается как помощник, чтобы выполнять вещи, которые Perl не делает или не делает достаточно быстро, но всегда подчиняется Perl. Чем ближе код XS придерживается этой модели, тем меньше вероятность возникновения конфликтов.
Одна из областей, где возникли конфликты, связана с локалями C. (См. perllocale.) Perl, за исключением одного случая и если ему не сказано иное, устанавливает базовую локаль, в которой выполняется программа, в локаль, переданную ему из среды. Это важное отличие от обычной C-программы, где базовая локаль — "C" локаль, если программа её не изменяет. Начиная с версии 5.20, эта базовая локаль полностью скрыта от чистого кода Perl вне лексического пространства use locale, за исключением нескольких вызовов функций в модуле POSIX, которые по необходимости используют её. Но базовая локаль, за этим исключением, доступна коду XS, влияя на все функции библиотеки C, поведение которых зависит от локали. Ваш код XS лучше не предполагает, что базовая локаль — "C". Исключение составляет категория локали LC_NUMERIC, и причина, по которой это исключение, заключается в том, что опыт показал, что это может быть проблематично для кода XS, в то время как у нас нет сообщений о проблемах с другими категориями локали. Причиной проблем с этой категорией является то, что символ, используемый в качестве десятичного разделителя, может меняться. Многие европейские языки используют запятую, в то время как английский и, следовательно, Perl ожидают точку (U+002E: ТОЧКА). Многие модули могут обрабатывать только радикс-символ в виде точки, поэтому Perl пытается сделать так. До Perl 5.20 попытка заключалась в установке LC_NUMERIC при запуске в "C" локаль. Любые другие изменения LC_NUMERIC могли её изменить; это вызывало некоторые сбои. Поэтому, начиная с v5.22, perl пытается поддерживать LC_NUMERIC всегда установленным на "C" для кода XS.
В заключение, вот что ожидать и как обращаться с локалями в коде XS:
- Не учитывающие локаль XS-коды
-
Помните, что даже если вы считаете, что ваш код не учитывает локаль, он может вызывать функцию библиотеки, которая это делает. Надеемся, что страница руководства для такой функции укажет на эту зависимость, но документация может быть неидеальной.
Текущая локаль доступна коду XS, за исключением, возможно,
LC_NUMERIC(объяснено в следующем абзаце). Проблемы с другими категориями не наблюдались. Perl инициализирует вещи при запуске, так что текущей локалью является та, которая указана в среде пользователя в тот момент. См. "ENVIRONMENT" в perllocale.Однако до версии v5.20 Perl инициализировал вещи при запуске, так что
LC_NUMERICбыл установлен в локаль "C". Но если какой-либо код где-либо изменил его, он оставался изменённым. Это означает, что ваш модуль не может полагаться наLC_NUMERIC, являющееся чем-то конкретным, и вы не можете ожидать, что числа с плавающей точкой (включая строки версий) будут содержать точки в качестве разделителей. Если вы не предусматриваете вариант без точки, ваш код может сломаться, если кто-нибудь где-нибудь изменит локаль. По этой причине в версии v5.22 поведение было изменено таким образом, что Perl пытается сохранитьLC_NUMERICв локали "C", за исключением операций внутри, где оно должно быть другим. Некорректно работающий XS-код всегда сможет изменить локаль, но наиболее распространённый случай этого проверяется и обрабатывается. - Учитывающие локаль XS-коды
-
Если требуется локаль из среды пользователя, коду XS не нужно устанавливать локаль, за исключением
LC_NUMERIC, так как perl уже настроила остальные. XS-код должен избегать изменения локали, так как это может негативно повлиять на другой, не связанный код, и может не быть потокобезопасным. Для минимизации проблем необходимо использовать макросы "STORE_LC_NUMERIC_SET_TO_NEEDED" в perlapi, "STORE_LC_NUMERIC_FORCE_TO_UNDERLYING" в perlapi и "RESTORE_LC_NUMERIC" в perlapi, чтобы внести необходимые изменения.Однако, начиная с Perl v5.28, локали являются потокобезопасными на платформах, поддерживающих эту функциональность. Windows поддерживает это начиная с Visual Studio 2005. Многие другие современные платформы поддерживают потокобезопасные функции POSIX 2008. C
#defineUSE_THREAD_SAFE_LOCALEбудут определены, если этот сборка использует эти функции. Из Perl-пространства, только для чтения переменная${SAFE_LOCALES}имеет значение 1, если сборка не является многопоточной или еслиUSE_THREAD_SAFE_LOCALEопределена; в противном случае она равна 0.С технической точки зрения, каждый поток может выбрать использование локали, специфичной для него (это функциональность Windows и POSIX 2008), или глобальную локаль, доступную всем потокам (это функциональность, которая всегда была). Реализации для Windows и POSIX совершенно разные. В Windows среда выполнения может быть настроена так, что стандартная функция
setlocale(3)знает только глобальную локаль или локаль для этого потока. В POSIX функцияsetlocaleвсегда работает с глобальной локалью, а для обработки локалей каждого потока созданы другие функции. Perl делает это прозрачным для кода в perl-пространстве. Он по-прежнему используетPOSIX::setlocale(), и интерпретатор переводит это в функции для каждого потока.Все остальные функции, чувствительные к локали, автоматически используют локаль каждого потока, если она включена, а в противном случае — глобальную локаль. Таким образом, вызовы
setlocaleнеэффективны на системах POSIX для текущего потока, если этот поток использует локаль каждого потока. Если perl скомпилирован для однопоточной работы, он не использует функции для каждого потока, поэтомуsetlocaleработает как ожидается.Если вы загрузили модуль
POSIX, вы можете использовать методы, описанные в perlcall, для вызоваPOSIX::setlocale, чтобы безопасно изменить или запросить локаль (на системах, где это безопасно), или вы можете использовать новую функцию 5.28 "Perl_setlocale" в perlapi вместо неё, что является прямым заменителем системной функцииsetlocale(3)и прозрачно обрабатывает однопоточные и многопоточные приложения.Есть некоторые вызовы библиотек, связанные с локалью, которые всё ещё не являются потокобезопасными, потому что они возвращают данные в буфер, общий для всех потоков. В прошлом это не имело значения, так как локали вообще не были потокобезопасными. Но теперь вам нужно об этом помнить, если ваш модуль вызывается в многопоточном приложении. Известные примеры:
asctime() ctime() gcvt() [POSIX.1-2001 only (function removed in POSIX.1-2008)] getdate() wcrtomb() if its final argument is NULL wcsrtombs() if its final argument is NULL wcstombs() wctomb()Некоторые из этих вызовов не должны выполняться в Perl-приложении, а для других уже существуют потокобезопасные версии:
asctime_r() ctime_r() Perl_langinfo()Формы
_rавтоматически используются, начиная с Perl 5.28, если вы компилируете свой код с#define PERL_REENTRANTСм. также "Perl_langinfo" в perlapi. Вы можете использовать методы, описанные в perlcall, чтобы получить лучшие доступные потокобезопасные версии этих функций
POSIX::localeconv() POSIX::wcstombs() POSIX::wctomb()И обратите внимание, что некоторые элементы, возвращаемые
Localeconv, доступны через "Perl_langinfo" в perlapi.Другие из этих вызовов не следует использовать в многопоточном приложении.
Некоторые модули могут вызывать библиотечную функцию, не являющуюся Perl-функцией, которая учитывает локаль. Это нормально, если она не пытается запросить или изменить локаль с помощью системной функции
setlocale. Но если они вызывают системную функциюsetlocale, эти вызовы могут быть неэффективными. Вместо этого,Perl_setlocaleработает во всех случаях. Простая функция setlocale неэффективна на многопоточных системах POSIX 2008. Она работает только с глобальной локалью, тогда как каждый поток имеет свою локаль, не обращая внимания на глобальную. Поскольку преобразование этих библиотек, не являющихся Perl-библиотеками, вPerl_setlocaleневозможно, в версии 5.28 есть новая функцияswitch_to_global_locale, которая переключится на поток, из которого она вызвана, так что любые системные вызовыsetlocaleбудут иметь желаемый эффект. Функцияsync_localeдолжна вызываться перед возвратом в Perl.Этот поток может изменять локаль как угодно, и это не повлияет на другие потоки, кроме тех, которые также были переключены на глобальную локаль. Это означает, что многопоточное приложение может иметь один поток, использующий чужую библиотеку, без проблем; но не более одного потока может быть таким образом занят. Вероятно, появятся плохие результаты.
В Perl без поддержки многопоточной локали некоторые внешние библиотеки, такие как
Gtk, изменяют локали. Это может вызвать проблемы для ядра Perl и других модулей. Для таких случаев, перед возвратом управления в Perl, начиная с версии v5.20.1, вызов функции sync_locale() из XS должен быть достаточным для предотвращения большинства этих проблем. До этого вам нужна чистая Perl-команда, которая это делает:POSIX::setlocale(LC_ALL, POSIX::setlocale(LC_ALL));или используйте методы, описанные в perlcall.
Версия XS
В этом документе описаны функции, поддерживаемые ExtUtils::ParseXS (также известный как xsubpp) 3.13_01.
АВТОР
Первоначально написан Деаном Роэрихом <roehrich@cray.com>.
Поддерживается с 1996 года членами команды Perl <perlbug@perl.org>.
© 1993–2020 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.28.3/perlxs