Spec-Zone.ru › Perl 5.38

perlxs

СОДЕРЖАНИЕ

  • ИМЯ
  • ОПИСАНИЕ
    • Введение
    • В пути
    • Анатомия XSUB
    • Стек аргументов
    • Переменная RETVAL
    • Возвращение SVs, AV и 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-функции (в стиле K&R). В таких случаях существует другой инструмент 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 от Dave Beazley может предоставить значительно более удобный механизм для создания кода связи расширения. Более подробную информацию можно найти на 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 в таких случаях. Было обнаружено, что это может привести к сегфолтам в случаях, когда 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» для выходных параметров (необходимых для параметров типа hash или array, которые должны быть созданы, если они не существовали). Если по какой-либо причине это поведение нежелательно, раздел 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:. Единственное исключение происходит, если это ; завершает строку, тогда это ; будет проигнорировано.

Следующий код демонстрирует, как предоставить код инициализации для параметров функции. Код инициализации eval'ится в двойных кавычках компилятором перед добавлением в выходные данные, поэтому все, что должно интерпретироваться буквально [в основном $, @, или \\], должно быть защищено обратными слешами. Переменные $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 идентичны параметрам, введённым с помощью "Оператора & Unary" и помещённым в раздел 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 для rpcb_gettime() XSUB может быть необязательным, поэтому эллипсис можно использовать для указания того, что 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 соответствует параметрам -prototypes и -noprototypes утилиты xsubpp. Это ключевое слово переопределяет параметры командной строки. Прототипы по умолчанию отключены. При включении прототипов 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

Будет выведено предупреждение, когда вы создадите более одного псевдонима для одного и того же значения. Это можно обойти, создав несколько определений, которые разрешаются до одного и того же значения, или с помощью современной версии ExtUtils::ParseXS, можно использовать символические псевдонимы, обозначаемые =>, а не =. Например, вы можете изменить приведенный выше фрагмент так, чтобы раздел псевдонимов выглядел так:

	ALIAS:
	    FOO::gettime = 1
	    BAR::getit = 2
            BAZ::gettime => FOO::gettime

что будет иметь тот же эффект, что и это:

	ALIAS:
	    FOO::gettime = 1
	    BAR::getit = 2
            BAZ::gettime = 1

за исключением того, что последний вариант выведет предупреждения во время процесса сборки. Механизм, который бы работал в обратной совместимости со старыми версиями нашей инструментальной цепочки, выглядел бы так:

    #define FOO_GETTIME 1
    #define BAR_GETIT 2
    #define BAZ_GETTIME 1

    bool_t
    rpcb_gettime(host,timep)
          char *host
          time_t &timep
	ALIAS:
	    FOO::gettime = FOO_GETTIME
	    BAR::getit = BAR_GETIT
            BAZ::gettime = BAZ_GETTIME
	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 содержит нашу функцию %%%CODE_BLOCK_333%%:

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, что и %%%CODE_BLOCK_346%%:

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);

Будет создан 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: ПОЛНАЯ ТОЧКА). Многие модули могут обрабатывать только радикс-символ, являющийся точкой, поэтому perl пытается сделать так, чтобы это было так. До Perl v5.20 попытка сводилась к установке LC_NUMERIC при запуске на локаль "C". Любые другие изменения setlocale() могли бы изменить её; это вызывало некоторые сбои. Поэтому, начиная с v5.22, perl пытается поддерживать LC_NUMERIC всегда установленной на "C" для кода XS.

Подводя итог, вот что можно ожидать и как обрабатывать локали в коде XS:

Независимый от локали код XS

Помните, что даже если вы считаете, что ваш код не зависит от локали, он может вызывать функцию библиотеки, которая зависит. Надеемся, что страница руководства по такой функции укажет на эту зависимость, но документация несовершенна.

Текущая локаль доступна коду XS, за исключением, возможно, LC_NUMERIC (объясняется в следующем абзаце). Не было сообщений о проблемах с другими категориями. Perl инициализирует вещи при запуске так, что текущей локалью является та, которая указана в среде пользователя в этот момент. См. "СРЕДА" в perllocale.

Однако до версии 5.20 Perl инициализировал вещи при запуске так, что LC_NUMERIC была установлена в локаль "C". Но если любой код где-либо изменит её, она останется изменённой. Это означает, что ваш модуль не может рассчитывать на LC_NUMERIC в чём-то определённом, и вы не можете ожидать, что числа с плавающей точкой (включая версии строк) будут содержать точки. Если вы не допускаете отсутствие точки, ваш код может сломаться, если кто-либо где-либо изменит локаль. По этой причине в версии 5.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 не рассматривается, в версии 5.28 есть новая функция switch_to_global_locale, которая переключит поток, из которого она вызвана, так что любые системные setlocale вызовы будут иметь желаемый эффект. Функция sync_locale должна быть вызвана перед возвращением в perl.

Этот поток может изменять локаль как ему заблагорассудится, и это не повлияет ни на какой другой поток, кроме тех, которые также были переключены на глобальную локаль. Это означает, что многопоточное приложение может иметь один поток, использующий внешнюю библиотеку без проблем; но не более одного потока может быть таким образом занят. Вероятны плохие результаты.

В perl без поддержки многопоточной локали некоторые внешние библиотеки, такие как Gtk, изменяют локали. Это может вызвать проблемы для ядра Perl и других модулей. Для них перед возвращением управления в perl, начиная с v5.20.1, вызов функции sync_locale() из XS должен быть достаточным для предотвращения большинства этих проблем. Ранее вам нужна чистая Perl-строка, которая делает это:

POSIX::setlocale(LC_ALL, POSIX::setlocale(LC_ALL));

или используйте методы, указанные в perlcall.

Версия XS

Этот документ охватывает функции, поддерживаемые ExtUtils::ParseXS (также известный как xsubpp) 3.51

Диагностика автора

Начиная с версии 3.49, некоторые предупреждения отключены по умолчанию. При разработке вы можете установить $ENV{AUTHOR_WARNINGS} в значение true в вашей среде или в вашем файле Makefile.PL, или установить $ExtUtils::ParseXS::AUTHOR_WARNINGS в true через код, или передать author_warnings=>1 в process_file() явно. В настоящее время это включит более строгую проверку псевдонимов, но в будущем могут быть добавлены дополнительные предупреждения. Этот тип предупреждений полезен только автору файла XS, а генерируемая диагностика не будет содержать деталей, специфичных для установки, поэтому она полезна только для разработчика кода XS.

Автор

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

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

© 1993–2023 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.38.0/perlxs

Spec-Zone.ru

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