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. Может потребоваться дополнительная типовая карта для обработки специальных структур и типов для связанной библиотеки. Дополнительную информацию о типовых картах см. в perlxstypemap.
Файл в формате XS начинается с раздела языка C, который продолжается до первой директивы MODULE =. После этой строки могут следовать другие директивы XS и определения XSUB. "Язык", используемый в этой части файла, обычно называется языком XS. xsubpp распознает и пропускает POD (см. perlpod) как в разделах языка C, так и в разделах языка XS, что позволяет файлу XS содержать встроенную документацию.
См. perlxstut для получения обучающего материала по всему процессу создания расширения.
Примечание: для некоторых расширений система SWIG Дэвида Бисли может предоставить значительно более удобный механизм для создания кода связи расширения. Дополнительную информацию см. на сайте http://www.swig.org/.
В пути
Многие из последующих примеров будут сосредоточены на создании интерфейса между Perl и библиотекой функций связывания ONC+ RPC. Функция 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 должен всегда гарантировать, что реальная функция 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 сгенерирует код, необходимый для обработки стека аргументов, встраивая фрагменты кода, найденные в таблицах типов. В более сложных случаях программист должен предоставить код.
Переменная 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 и HV через 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 для любого из этих типов с версией, которая имеет надлежащую обработку счётчиков ссылок. В вашем разделе 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 *. Справочная документация по всем основным таблицам типов доступна в 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: также позволит выходному параметру быть сопоставленным с соответствующим фрагментом кода, а не с таблицей типов.
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' для выходных параметров (необходимую для параметров типа hash или array, которые должны быть созданы, если они не существовали). Если по какой-то причине это поведение нежелательно, в разделе 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-функция ничего не возвращает при успехе и вызовет die() с осмысленным сообщением об ошибке при ошибке.
Ключевое слово 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.
Для поддержки потенциально сложных typemap, если запись 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: не должны использоваться вместе в одном XSUB.
Фактическое различие между секциями PPCODE: и CODE: заключается в инициализации макроса SP (который обозначает текущий указатель стека Perl) и в обработке данных в стеке при возвращении из XSUB. В секциях CODE: SP сохраняет значение, которое было при входе в XSUB: SP находится на указателе функции (после последнего параметра). В секциях PPCODE: SP перемещается назад к началу списка параметров, что позволяет макросам PUSH*() размещать выходные значения в том месте, где Perl ожидает их при возвращении XSUB в Perl.
Сгенерированный трейлер для секции CODE: гарантирует, что количество возвращаемых значений, которые увидит Perl, равно либо 0, либо 1 (в зависимости от void-ности значения возврата 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 будет правильно откорректирован. Для других макросов XSRETURN см. perlapi.
Поскольку макросы 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: выше, но может быть использовано для принудительного использования xsubpp конкретного прототипа для 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, который находится первым в вашем пути, а не обязательно тот же 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. Объект должен быть создан в C++ функцией new() и должен быть освящён 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 v5.20 попытка заключалась просто в установке LC_NUMERIC при запуске в локаль "C". Любой другой setlocale() изменил бы её; это вызвало некоторые сбои. Поэтому, начиная с v5.22, Perl пытается всегда поддерживать LC_NUMERIC установленной на "C" для кода XS.
Подводя итог, вот что ожидать и как обрабатывать локали в коде XS:
- Нелокально-зависимый код XS
-
Помните, что даже если вы думаете, что ваш код не зависит от локали, он может вызывать библиотечную функцию, которая зависит. Надеюсь, страница справки по такой функции укажет на эту зависимость, но документация может быть неполной.
Текущая локали доступна коду XS, за исключением, возможно,
LC_NUMERIC(объясняется в следующем абзаце). Проблемы с другими категориями не наблюдались. Perl инициализирует вещи при запуске, так что текущая локали соответствует той, что указана в среде пользователя в этот момент. См. "СРЕДА" в 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для безопасного изменения или запроса локали (на системах, где это безопасно), или вы можете использовать новую функцию v5.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— это не вариант, в v5.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.
АВТОР
Изначально написана Dean Roehrich <roehrich@cray.com>.
Поддерживается с 1996 года The Perl Porters <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.30.3/perlxs