perlpragma
СОДЕРЖАНИЕ
ИМЯ
perlpragma - как написать пользовательский прагма
ОПИСАНИЕ
Прагма — это модуль, который влияет на некоторые аспекты поведения Perl во время компиляции или выполнения, такие как strict или warnings. С Perl 5.10 вы больше не ограничены встроенными прагмами; теперь вы можете создавать пользовательские прагмы, которые изменяют поведение пользовательских функций в лексическом пространстве.
Базовый пример
Например, предположим, что вам нужно создать класс, реализующий перегруженные математические операторы, и вы хотите предоставить свой собственный прагма, который работает примерно как use integer;. Вам нужен такой код
use MyMaths;
my $l = MyMaths->new(1.2);
my $r = MyMaths->new(3.4);
print "A: ", $l + $r, "\n";
use myint;
print "B: ", $l + $r, "\n";
{
no myint;
print "C: ", $l + $r, "\n";
}
print "D: ", $l + $r, "\n";
no myint;
print "E: ", $l + $r, "\n"; чтобы получить вывод
A: 4.6
B: 4
C: 4.6
D: 4
E: 4.6 т.е., где use myint; активен, операции сложения принудительно преобразуются в целые числа, в то время как по умолчанию они не преобразуются, при этом поведение по умолчанию восстанавливается с помощью no myint;
Минимальная реализация пакета MyMaths будет примерно такой:
package MyMaths;
use v5.36;
use myint();
use overload '+' => sub {
my ($l, $r) = @_;
# Pass 1 to check up one call level from here
if (myint::in_effect(1)) {
int($$l) + int($$r);
} else {
$$l + $$r;
}
};
sub new {
my ($class, $value) = @_;
bless \$value, $class;
}
1; Обратите внимание, как мы загружаем пользовательский прагма myint со пустым списком () для предотвращения вызова его import.
Взаимодействие с компиляцией Perl происходит внутри пакета myint:
package myint;
use v5.36;
sub import {
$^H{"myint/in_effect"} = 1;
}
sub unimport {
$^H{"myint/in_effect"} = 0;
}
sub in_effect {
my $level = shift // 0;
my $hinthash = (caller($level))[10];
return $hinthash->{"myint/in_effect"};
}
1; Поскольку прагмы реализуются как модули, как и любой другой модуль, use myint; превращается в
BEGIN {
require myint;
myint->import();
} и no myint; равно
BEGIN {
require myint;
myint->unimport();
} Следовательно, процедуры import и unimport вызываются во время компиляции для кода пользователя.
Пользовательские прагмы хранят свое состояние, записывая в магический массив %^H, поэтому эти две процедуры манипулируют им. Информация о состоянии в %^H хранится в дереве синтаксического анализа, и ее можно получать только для чтения во время выполнения с помощью caller(), в индексе 10 списка возвращаемых результатов. В примере прагмы получение инкапсулировано в процедуру in_effect(), которая принимает в качестве параметра количество кадров вызова, чтобы подняться вверх, чтобы найти значение прагмы в скрипте пользователя. Это использует caller() для определения значения $^H{"myint/in_effect"} при вызове каждой строки скрипта пользователя, и, следовательно, обеспечивает правильную семантику в подпрограмме, реализующей перегруженное сложение.
Именование ключей
Существует только один %^H, но произвольно много модулей, которые хотят использовать его семантику области видимости. Чтобы не мешать друг другу, они должны убедиться, что используют разные ключи в массиве. Поэтому принято, чтобы модуль использовал только ключи, начинающиеся с имени модуля (имя его основного пакета) и символа "/". После этого префикса, определяющего модуль, остальная часть ключа полностью зависит от модуля: она может содержать любые символы. Например, модуль Foo::Bar должен использовать ключи, такие как Foo::Bar/baz и Foo::Bar/$%/_!. Модули, которые следуют этой соглашению, все хорошо взаимодействуют друг с другом.
Ядро Perl использует несколько ключей в %^H, которые не следуют этой соглашению, потому что они предшествуют ей. Ключи, которые следуют соглашению, не будут конфликтовать с историческими ключами ядра.
Детали реализации
Дерево синтаксического анализа делится между потоками. Это означает, что существует вероятность того, что дерево синтаксического анализа переживёт конкретный поток (и, следовательно, экземпляр интерпретатора), который его создал, поэтому истинные скаляры Perl не могут храниться в дереве синтаксического анализа. Вместо этого используется компактная форма, которая может хранить только значения, которые являются целыми числами (знаковыми и беззнаковыми), строками или undef — ссылки и числа с плавающей точкой строятся в строки. Если вам нужно хранить несколько значений или сложные структуры, вы должны их сериализовать, например, с помощью pack. Удаление ключа массива из %^H записывается, и, как всегда, его можно отличить от существования ключа со значением undef с помощью exists.
Не пытайтесь хранить ссылки на структуры данных в виде целых чисел, которые извлекаются через caller и преобразуются обратно, так как это не будет потокобезопасным. Доступ осуществлялся к структуре без блокировки (что небезопасно для скаляров Perl), и либо структура должна утечь, либо она должна быть освобождена при завершении потока, который её создал, что может произойти до удаления ссылающегося на неё дерева синтаксического анализа, если другие потоки переживут его.
© 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/perlpragma