PerlIO実装メモ#3 PerlIOレイヤのデータ構造

PerlIOのデータ構造は以下の通り。

typedef struct _PerlIO PerlIOl;
typedef struct _PerlIO_funcs PerlIO_funcs;
typedef PerlIOl* PerlIO;

struct _PerlIO
{
    PerlIOl*      next;  /* Lower layer */
    PerlIO_funcs* tab;   /* Functions for this layer */
    U32           flags; /* Various flags for state */
};

ここで、"PerlIO* fp;"という変数があるとすると、fpの型は"PerlIO*"であり、"PerlIOl**"であり、"struct _PerlIO**"である。ここで、"PerlIOl"はリンクリストとなっており、PerlIO_push()は以下のような処理を行う。

/*  オリジナルのPerlIO_push()の簡略版 */
PerlIO*
PerlIO_push(pTHX_ PerlIO *fp, PerlIO_funcs* tab, const char *mode, SV *arg){
    PerlIOl* layer = (PerlIOl*)malloc(tab->size);
    layer->next = *fp;
    *fp = layer;

    layer->tab = (PerlIO_funcs*) tab;
    layer->tab->Pushed(aTHX_ fp, mode, arg, tab);

    return fp;
}

まず、前半の3文はPerlIOクラスのインスタンスであるtabから得たPerlIOレイヤインスタンスのサイズ分のメモリを確保し、新しく割り当てたlayerのnextに元のレイヤーを代入しつつ、fpの参照先を新しいlayerで置き換える。PerlIO*がPerlIOlのポインタのポインタとなっているのは、PerlIO*がリンクリストの最上部を参照しているからだ。
そして、後半2文ではPerlIOオブジェクトのクラス(tab)を設定し、イニシャライザメソッドPushed()を呼んでいる*1
もっとも、このレベルのデータ構造にはマクロを使ってアクセスするので、通常はあまりデータ構造を意識することはない。

#define PerlIOBase(f)      (*(f))
#define PerlIOSelf(f,type) ((type *)PerlIOBase(f))
#define PerlIONext(f)      (&(PerlIOBase(f)->next))
#define PerlIOValid(f)     ((f) && *(f))

ただし、perlio.cを読むつもりなら、データ構造を理解しなければらないない。マクロを使わずにアクセスしている箇所が多々あるためだ*2

See Also perliol / Data Structures.

*1:Pushed()の引数tabが必要になるケースがあるとは思えないが、この辺りは歴史的経緯かもしれない。

*2:v5.10.0 現在