perlpragma - Как писать пользовательские прагмы
Прагма - это модуль, который влияет на некоторые моменты во время компиляции и во время выполнения Perl-кода. Пример прагмы: strictили warnings. С выходом Perl 5.10 вы не ограничены в разработке прагм, теперь можете создавать пользовательские прагмы, которые будут менять поведение пользовательских функций в лексическом контексте.
strict
warnings
Например, вам нужно создать класс, который реализует перегрузку математических операторов, и вы хотели бы использовать свою прагму с функционалом похожим на use integer;. Пример кода:
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;
use myint;
no myint;
Минимальная реализация пакета MyMaths будет примерно такой:
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 не будет вызвана.
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 вызываются во время компиляции пользовательского кода.
unimport
Пользовательские прагмы сохраняют свое состояние в магическом хеше %^H, следовательно эти две подпрограммы управляют им. Информация о состоянии в %^H сохраняется в op-дереве, и может быть получено во время выполнения с помощью caller(), под индексом 10 в возвращённом списке. В прагме из примера восстановление инкапсулировано в подпрограмме in_effect(), которая в качестве аргументов принимает единственный параметр - число вызовов, оставшихся для нахождения значения прагмы в пользовательском скрипте. Здесь используется caller(), чтобы определить значение $^H{"myint/in_effect"}, когда каждая строка пользовательского скрипта была вызвана. Поэтому в подпрограмме, реализующей перегрузку оператора сложения, используется корректная семантика.
%^H
caller()
in_effect()
$^H{"myint/in_effect"}
Существует только один %^H, но сколь угодно много модулей, которые хотят использовать его обзорного семантики. Чтобы не наступать друг другу на пальцы, они должны быть уверены, чтобы использовать различные ключи в хэше. Поэтому для обычного модуля для использования только ключи, которые начинаются с имени модуля (имя его основной пакет) и символ "/". После этого модуль идентифицирующей префикс, остальная часть ключа полностью зависит от модуля: он может включать любые символы. Например, модуль Foo::Bar должны использовать ключи, такие как Foo::Bar/baz и Foo::Bar/$%/_!. Модули следующие этому соглашению все дружат друг с другом.
Foo::Bar
Foo::Bar/baz
Foo::Bar/$%/_!
Ядро Perl использует несколько ключей в %^H, которые не следуют этому соглашению, потому что они предшествуют его. Ключи, которые следуют Конвенция не вступит в противоречие с историческими ключей ядра.
op-дерево является общим для всех потоков. Это означает, что существует возможность, при которой op-дерево "переживет" поток (и следовательно экземпляр интерпретатора), породивший его.Таким образом, настоящие Perl-скаляры не могут храниться в op-дереве. Вместо этого используется компактная форма, которая может хранить только целые значения (со знаком или без), строки или undef; ссылки и числа с плавающей точкой преобразуются в строку. Если вам нужно хранить составные значения или сложные структуры, вам следует сериализовать их , например с помощью pack. Ключи хеша из %^H можно удалять и, как всегда, с помощью exists можно понять, является ли это значение определённым или undef.
undef
pack
exists
Не пытайтесь хранить указатели на структуры данных как целые числа, которые получены из caller и преобразованы обратно, т.к. это будет не безопасным для потоков. Доступ к структуре будет неблокирующим (что не безопасно для Perl скаляров). Такие структуры могут давать утечки памяти, либо быть освобождены, когда породивший их поток завершится. Это может произойти до того, как op-дерево удалит ссылки на них, если его поток переживёт их.
caller
Николай Мишин <mishin@cpan.org>
<mishin@cpan.org>
Анатолий Шарифулин <sharifulin@gmail.com>
<sharifulin@gmail.com>
Алексей Суриков (Language) <KSURi>
<KSURi>
Михаил Любимов (Language) <mikhail.lyubimov>
<mikhail.lyubimov>
Дмитрий Константинов (Language) <Dim_K>
<Dim_K>
Евгений Баранов (Language) <Baranov>
<Baranov>
To install POD2::RU, copy and paste the appropriate command in to your terminal.
cpanm
cpanm POD2::RU
CPAN shell
perl -MCPAN -e shell install POD2::RU
For more information on module installation, please visit the detailed CPAN module installation guide.