Spec-Zone.ru › Perl 5.38

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 v5.36;
    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 v5.36;

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–2023 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.38.0/perlpragma

Spec-Zone.ru

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