Spec-Zone.ru › Perl 5.32

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, которые не следуют этой конвенции, потому что они существовали раньше. Ключи, соответствующие конвенции, не будут конфликтовать с историческими ключами ядра.

Детали реализации

Дерево синтаксического анализа (optree) используется совместно между потоками. Это означает, что существует вероятность того, что дерево синтаксического анализа переживёт конкретный поток (и, следовательно, экземпляр интерпретатора), который его создал, поэтому истинные скаляры 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.30.3/perlpragma

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API