目錄

名稱

perlpragma - 如何撰寫使用者 pragma

說明

pragma 是個模組,會影響 Perl 編譯時間或執行時間的某個面向,例如 strictwarnings。從 Perl 5.10 開始,您不再受限於內建的 pragma;現在您可以建立使用者 pragma,在詞法範圍內修改使用者函式的行為。

基本範例

例如,假設您需要建立一個實作重載數學運算子的類別,並希望提供一個自己的 pragma,其功能很像 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();
}

因此,importunimport 常式會在使用者的程式碼中於編譯時間呼叫。

使用者實用程式會透過寫入神奇雜湊 %^H 來儲存其狀態,因此這兩個常式會操作它。%^H 中的狀態資訊會儲存在 optree 中,並可以在執行階段使用 caller() 以唯讀方式擷取,在傳回結果清單的索引 10 中。在範例實用程式中,擷取會封裝到常式 in_effect() 中,它會將呼叫架構的數量當作參數,以找出使用者的腳本中實用程式的值。這會使用 caller() 來判斷使用者腳本的每一行呼叫時 $^H{"myint/in_effect"} 的值,因此會在實作重載加法的子常式中提供正確的語意。

金鑰命名

只有一個 %^H,但有任意多個模組想要使用其範圍語意。為了避免互相踩到對方的腳,它們需要確定在雜湊中使用不同的金鑰。因此,模組慣例上只使用以模組名稱 (其主要套件的名稱) 和「/」字元開頭的金鑰。在此模組識別前綴之後,金鑰的其餘部分完全取決於模組:它可以包含任何字元。例如,模組 Foo::Bar 應該使用例如 Foo::Bar/bazFoo::Bar/$%/_! 的金鑰。遵循此慣例的模組都彼此相安無事。

Perl 核心在 %^H 中使用少數金鑰,它們不遵循此慣例,因為它們早於此慣例。遵循慣例的金鑰不會與核心的歷史金鑰衝突。

實作詳細資料

optree 在執行緒之間共享。這表示 optree 有可能比建立它的特定執行緒(因此也比解釋器執行個體)存活更久,因此真正的 Perl 標量無法儲存在 optree 中。相反地,會使用一種精簡的格式,它只能儲存整數(有號和無號)、字串或 undef 的值 - 參照和浮點值會字串化。如果您需要儲存多個值或複雜的結構,您應該序列化它們,例如使用 pack。從 %^H 刪除雜湊鍵會被記錄下來,而且一如往常,可以使用 exists 將它與值為 undef 的鍵的存在區分開來。

不要嘗試將參照儲存到資料結構中,作為透過 caller 擷取並轉換回來的整數,因為這不會執行緒安全。存取會在不鎖定的情況下進行(這對 Perl 的標量來說不安全),而且結構必須外洩,或者必須在建立它的執行緒終止時釋放它,這可能早於參照它的 optree 被刪除,如果其他執行緒比它存活更久的話。