perlpragma |
- Statistics
- Participants
- Translate into Russian
- Translation result
- Translation complete.
=head1 НАЗВАНИЕ
perlpragma - Как писать пользовательские прагмы
=head1 ОПИСАНИЕ
Прагма - это модуль, который влияет на некоторые моменты во время компиляции и во время выполнения Perl-кода. Пример прагмы: C<strict>или C<warnings>. С выходом Perl 5.10 вы не ограничены в разработке прагм, теперь можете создавать пользовательские прагмы, которые будут менять поведение пользовательских функций в лексическом контексте.
=head1 Основной пример
Например, вам нужно создать класс, который реализует перегрузку математических операторов, и вы хотели бы использовать свою прагму с функционалом похожим на C<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
I<В примере>, в котором используется C<use myint;>, оператор сложения работает с целыми числами, значения по умолчанию не определены. Поведение по умолчанию будет восстановлено C<no myint;>
Минимальная реализация пакета C<MyMaths> будет примерно такой:
package MyMaths;
use warnings;
use strict;
use myint();
use overload '+' => sub {
my ($l, $r) = @_;
# проверка вызова
if (myint::in_effect(1)) {
int($$l) + int($$r);
} else {
$$l + $$r;
}
};
sub new {
my ($class, $value) = @_;
bless \$value, $class;
}
1;
Примечание. При загрузке пользовательской прагмы C<myint> без параметров С<()> функция C<import> не будет вызвана.
Взаимодействие с Perl во время компиляции внутри пакета C<myint>:
package myint;
use strict;
use warnings;
sub import {
$^H{myint} = 1;
}
sub unimport {
$^H{myint} = 0;
}
sub in_effect {
my $level = shift // 0;
my $hinthash = (caller($level))[10];
return $hinthash->{myint};
}
1;
Прагма реализована как модуль, поэтому C<use myint;> означает:
BEGIN {
require myint;
myint->import();
}
соответственно, C<no myint;>:
BEGIN {
require myint;
myint->unimport();
}
Следовательно, C<import> и C<unimport> вызываются B<во время компиляции> пользовательского кода.
Пользовательские прагмы сохраняют свое состояние в магическом хеше C<%^H>, следовательно эти две подпрограммы управляют им. Информация о состоянии в C<%^H> сохраняется в op-дереве, и может быть получено во время выполнения с помощью C<caller()>, под индексом 10 в возвращённом списке. В прагме из примера восстановление инкапсулировано в подпрограмме C<in_effect()>, которая в качестве аргументов принимает единственный параметр - число вызовов, оставшихся для нахождения значения прагмы в пользовательском скрипте. Здесь используется C<caller()>, чтобы определить значение C<$^H{myint}>, когда каждая строка пользовательского скрипта была вызвана. Поэтому в подпрограмме, реализующей перегрузку оператора сложения, используется корректная семантика.
=head1 Детали реализации
op-дерево является общим для всех потоков. Это означает, что существует возможность, при которой op-дерево "переживет" поток (и следовательно экземпляр интерпретатора), породивший его. Таким образом, настоящие Perl-скаляры не могут храниться в op-дереве. Вместо этого используется компактная форма, которая может хранить только целые значения (со знаком или без), строки или C<undef>; ссылки и числа с плавающей точкой преобразуются в строку. Если вам нужно хранить составные значения или сложные структуры, вам следует сериализовать их , например с помощью C<pack>. Ключи хеша из C<%^H> можно удалять и, как всегда, с помощью C<exists> можно понять, является ли это значение определённым или C<undef>.
B<Не> пытайтесь хранить указатели на структуры данных как целые числа, которые получены из C<caller> и преобразованы обратно, т.к. это будет не безопасным для потоков. Доступ к структуре будет неблокирующим (что не безопасно для Perl скаляров). Такие структуры могут давать утечки памяти, либо быть освобождены, когда породивший их поток завершится. Это может произойти до того, как op-дерево удалит ссылки на них, если его поток переживёт их.
