НАИМЕНОВАНИЕ
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) = @_;
# передать 1, чтобы отметить один вызов уровня отсюда
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();
}
and no myint;
is
BEGIN {
require myint;
myint->unimport();
}
Следовательно, import
и unimport
вызываются во время компиляции пользовательского кода.
Пользовательские прагмы сохраняют свое состояние в магическом хеше %^H
, следовательно эти две подпрограммы управляют им. Информация о состоянии в %^H
сохраняется в op-дереве, и может быть получено во время выполнения с помощью caller()
, под индексом 10 в возвращённом списке. В прагме из примера восстановление инкапсулировано в подпрограмме in_effect()
, которая в качестве аргументов принимает единственный параметр - число вызовов, оставшихся для нахождения значения прагмы в пользовательском скрипте. Здесь используется caller()
, чтобы определить значение $^H{"myint/in_effect"}
, когда каждая строка пользовательского скрипта была вызвана. Поэтому в подпрограмме, реализующей перегрузку оператора сложения, используется корректная семантика.
Ключевые наименования
Существует только один %^H
, но сколь угодно много модулей, которые хотят использовать его обзорного семантики. Чтобы не наступать друг другу на пальцы, они должны быть уверены, чтобы использовать различные ключи в хэше. Поэтому для обычного модуля для использования только ключи, которые начинаются с имени модуля (имя его основной пакет) и символ "/". После этого модуль идентифицирующей префикс, остальная часть ключа полностью зависит от модуля: он может включать любые символы. Например, модуль Foo::Bar
должны использовать ключи, такие как Foo::Bar/baz
и Foo::Bar/$%/_!
. Модули следующие этому соглашению все дружат друг с другом.
Ядро Perl использует несколько ключей в %^H
, которые не следуют этому соглашению, потому что они предшествуют его. Ключи, которые следуют Конвенция не вступит в противоречие с историческими ключей ядра.
Детали реализации
op-дерево является общим для всех потоков. Это означает, что существует возможность, при которой op-дерево "переживет" поток (и следовательно экземпляр интерпретатора), породивший его.Таким образом, настоящие Perl-скаляры не могут храниться в op-дереве. Вместо этого используется компактная форма, которая может хранить только целые значения (со знаком или без), строки или undef
; ссылки и числа с плавающей точкой преобразуются в строку. Если вам нужно хранить составные значения или сложные структуры, вам следует сериализовать их , например с помощью pack
. Ключи хеша из %^H
можно удалять и, как всегда, с помощью exists
можно понять, является ли это значение определённым или undef
.
Не пытайтесь хранить указатели на структуры данных как целые числа, которые получены из caller
и преобразованы обратно, т.к. это будет не безопасным для потоков. Доступ к структуре будет неблокирующим (что не безопасно для Perl скаляров). Такие структуры могут давать утечки памяти, либо быть освобождены, когда породивший их поток завершится. Это может произойти до того, как op-дерево удалит ссылки на них, если его поток переживёт их.
ПЕРЕВОДЧИКИ
Николай Мишин
<mishin@cpan.org>
Анатолий Шарифулин
<sharifulin@gmail.com>
Алексей Суриков (Language)
<KSURi>
Михаил Любимов (Language)
<mikhail.lyubimov>
Дмитрий Константинов (Language)
<Dim_K>
Евгений Баранов (Language)
<Baranov>