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 warnings;
use strict;
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 strict;
use warnings;
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.34.0/perlpragma