Spec-Zone.ru › Perl 5.36

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
      • ССЫЛКА MY_CXT
    • Интерфейсы систем, учитывающие потоки
  • ПРИМЕРЫ
  • ОСОБЕННОСТИ
  • ВЕРСИЯ 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 (в стиле К. и Р.). В таких обстоятельствах есть другой инструмент, h2xs, который может преобразовать весь заголовочный файл C в соответствующий файл XS, который обеспечит соединение с функциями/макросами, описанными в заголовочном файле.

Компилятор XS называется xsubpp. Этот компилятор создаёт конструкции, необходимые для того, чтобы XSUB мог манипулировать значениями Perl, и создаёт необходимые соединения, чтобы Perl мог вызвать XSUB. Компилятор использует typemaps для определения того, как сопоставлять параметры функций C и значения вывода со значениями Perl и обратно. По умолчанию typemap (входящий в Perl) обрабатывает многие распространённые типы C. Дополнительный typemap может потребоваться для обработки любых специальных структур и типов для подключаемой библиотеки. Для получения дополнительной информации о typemaps см. perlxstypemap.

Файл в формате XS начинается с секции кода C, которая продолжается до первой директивы MODULE =. После этой строки могут следовать другие директивы XS и определения XSUB. «Язык», используемый в этой части файла, обычно называется языком XS. xsubpp распознаёт и пропускает POD (см. perlpod) как в секциях C, так и в секциях языка XS, что позволяет файлу XS содержать встроенную документацию.

См. perlxstut для получения руководства по всему процессу создания расширений.

Примечание: для некоторых расширений система SWIG Дэйва Бисли может предоставить значительно более удобный механизм для создания связующего кода расширения. См. http://www.swig.org/ для получения дополнительной информации.

Для простых связей с библиотеками C, а также другими библиотеками машинного кода, рассмотрите использование более простого интерфейса libffi через модули CPAN, такие как FFI::Platypus или FFI::Raw.

В пути

Многие последующие примеры будут сосредоточены на создании интерфейса между 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.) Чтобы упростить вашу работу, файл карты типов автоматически делает 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) в файле карты типов, счётчик ссылок на AV * не уменьшается должным образом. Таким образом, вышеупомянутый XSUB будет утечка памяти всякий раз, когда он вызывается. Такая же проблема существует для HV *, CV *, и SVREF (что указывает на скалярную ссылку, а не на общую SV *). В коде XS в perl, начиная с perl 5.16, вы можете переопределить карты типов для любого из этих типов с версией, которая имеет правильную обработку счётчиков ссылок. В вашем разделе 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» для выходных параметров (необходимое для параметров типа хэш или массив, которые должны быть созданы, если они не существовали). Если по какой-либо причине это поведение нежелательно, раздел OUTPUT может содержать строку SETMAGIC: DISABLE, чтобы отключить её для оставшихся параметров в разделе OUTPUT. Аналогично, SETMAGIC: ENABLE может использоваться для её повторного включения для оставшихся параметров в разделе OUTPUT. См. perlguts для получения дополнительной информации о магической функции «set».

Ключевое слово 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, вы можете встраивать typemap в свой XS-код вместо или в дополнение к typemap в отдельном файле. Несколько таких встроенных typemap будут обрабатываться в порядке появления в XS-коде, и, как локальные файлы typemap, имеют приоритет над стандартным typemap, встроенные typemap могут перезаписывать предыдущие определения секций TYPEMAP, INPUT и OUTPUT. Синтаксис встроенных typemap

TYPEMAP: <<HERE
... your typemap code here ...
HERE

где ключевое слово TYPEMAP должно появиться в первом столбце новой строки.

См. perlxstypemap для получения подробной информации о написании typemap.

Инициализация параметров функции

Параметры C-функции обычно инициализируются значениями из стека аргументов (который, в свою очередь, содержит параметры, переданные XSUB из Perl). Typemap содержат фрагменты кода, используемые для преобразования значений Perl в параметры C. Однако программист может переопределить typemap и предоставить альтернативный (или дополнительный) код инициализации. Код инициализации начинается с первого =, ; или + в строке в разделе INPUT:. Единственное исключение происходит, если это ; завершает строку, тогда это ; игнорируется.

Следующий код демонстрирует, как предоставить код инициализации для параметров функции. Код инициализации выполняется в двойных кавычках компилятором перед добавлением в вывод, поэтому все, что должно интерпретироваться буквально [в основном $, @, или \\], должно быть защищено обратными слешами. Переменные $var, $arg, и $type могут использоваться, как в typemap.

bool_t
rpcb_gettime(host,timep)
     char *host = (char *)SvPVbyte_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}) ? SvPVbyte_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.

Следующие примеры эквивалентны, но если код использует сложные typemap, то первый пример безопаснее.

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 *)SvPVbyte_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 (в зависимости от наличия значения возврата функции 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'. Смотрите perlguts для получения подробной информации о магии 'set'.

Возвращение 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: соответствует параметрам -versioncheck и -noversioncheck xsubpp. Это ключевое слово переопределяет параметры командной строки. Проверка версий включена по умолчанию. Когда проверка версий включена, модуль 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. Для получения информации о Perl-прототипах обратитесь к разделу "Прототипы" в perlsub.

bool_t
rpcb_gettime(timep, ...)
      time_t timep = NO_INIT
    PROTOTYPE: $;$
    PREINIT:
      char *host = "localhost";
    CODE:
              if( items > 1 )
                   host = (char *)SvPVbyte_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:, а все, что следует за последним 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);

Для typedef будет создан struct netconfig. Объект 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_t равно

typedef struct {
    int index;
} my_cxt_t;

то используйте это для доступа к члену index.

dMY_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"

/* Note: On glibc 2.13 and earlier, this needs be <rpc/rpc.h> */
#include <tirpc/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";

В Makefile.PL добавьте -ltirpc и -I/usr/include/tirpc.

ОСОБЕННОСТИ

Код 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: ТОЧКА). Многие модули могут обрабатывать только radix-символ как точку, и поэтому perl пытается сделать так, чтобы это было так. До версии Perl 5.20 попытка сводилась к установке LC_NUMERIC при запуске в локаль "C". Любой другой setlocale() изменил бы ее; это вызвало некоторые сбои. Поэтому, начиная с 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 #define USE_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.

АВТОР

Первоначально написана Дином Роэрихом <roehrich@cray.com>.

Поддерживается с 1996 года The Perl Porters <perl5-porters@perl.org>.

© 1993–2021 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.36.0/perlxs

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API