=encoding utf8

=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) = @_;
	# передать 1, чтобы отметить один вызов уровня отсюда
        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/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;

Прагма реализована как модуль, поэтому C<use myint;> означает:

    BEGIN {
        require myint;
        myint->import();
    }

and C<no myint;> is

    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/in_effect"}>, когда каждая строка пользовательского скрипта была вызвана. Поэтому в подпрограмме, реализующей перегрузку оператора сложения, используется корректная семантика.

=head1 Ключевые наименования

Существует только один C<%^H>, но сколь угодно много модулей, которые хотят использовать его обзорного семантики.  Чтобы не наступать друг другу на пальцы, они должны быть уверены, чтобы использовать различные ключи в хэше.  Поэтому для обычного модуля для использования только ключи, которые начинаются с имени модуля (имя его основной пакет) и символ "/".  После этого модуль идентифицирующей префикс, остальная часть ключа полностью зависит от модуля: он может включать любые символы.  Например, модуль C<Foo::Bar> должны использовать ключи, такие как C<Foo::Bar/baz> и C<Foo::Bar/$%/_!>.
Модули следующие этому соглашению все дружат друг с другом.

Ядро Perl использует несколько ключей в C<%^H>, которые не следуют этому соглашению, потому что они предшествуют его.  Ключи, которые следуют Конвенция не вступит в противоречие с историческими ключей ядра.

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

op-дерево является общим для всех потоков.  Это означает, что существует возможность, при которой op-дерево "переживет" поток (и следовательно экземпляр интерпретатора), породивший его.Таким образом, настоящие Perl-скаляры не могут храниться в op-дереве.  Вместо этого используется компактная форма, которая может хранить только целые значения (со знаком или без), строки или C<undef>; ссылки и числа с плавающей точкой преобразуются в строку.  Если вам нужно хранить составные значения или сложные структуры, вам следует сериализовать их , например с помощью C<pack>.
Ключи хеша из C<%^H> можно удалять и, как всегда, с помощью C<exists> можно понять, является ли это значение определённым или C<undef>.

B<Не> пытайтесь хранить указатели на структуры данных как целые числа, которые получены из C<caller> и преобразованы обратно, т.к. это будет не безопасным для потоков.
Доступ к структуре будет неблокирующим (что не безопасно для Perl скаляров). Такие структуры могут давать утечки памяти, либо быть освобождены, когда породивший их поток завершится. Это может произойти до того, как op-дерево удалит ссылки на них, если его поток переживёт их.

=cut

=head1 ПЕРЕВОДЧИКИ

=over

=item * Николай Мишин (Tech & Language Lead)
=item * Анатолий Шарифулин (Tech Lead), C< sharifulin + sharifulin at gmail.com >
=item * Николай Мишин (Language Lead), C< mishin + pod2ru at gmail.com, mi at ya.ru >
=item * Алексей Суриков (Language), C< KSURi>
=item * Михаил Любимов (Language), C< mikhail.lyubimov >
=item * Дмитрий Константинов (Language), C< Dim_K >
=item * Евгений Баранов (Language), C< Baranov >

=back    