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–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlpragma