【PL/I深層解説】 EXTERNAL属性による変数共有と、リンケージの深淵
現場で「なぜか値が化ける」というバグに遭遇したとき、多くのエンジニアはまずレコード入出力の読み込みミスを疑う。だが、大規模なバッチ改修の現場で長年揉まれてきた私から言わせれば、真の魔物はもっと別の場所、特に「EXTERNAL属性」の不適切な管理に潜んでいることが多い。
今日は、PL/Iの「予約語を持たない」という極めて特異な言語仕様と、それゆえに発生するリンケージ時のトラップ、そして実務で安全にグローバル変数を共有する作法について話をしよう。
—
なぜPL/Iには「予約語」がないのか
まず初心者が驚くのが、「IF」や「THEN」といったキーワードを、そのまま変数名として使えてしまう点だ。これはPL/Iの設計思想が「文脈(Context)」を重視しているからに他ならない。
コンパイラは「このIFはキーワードか、それとも変数名か?」を前後の構文から判断する。非常に柔軟だが、これは諸刃の剣だ。新人プログラマーが安易にキーワードを変数名に使うと、デバッグ時にコンパイラがどの解釈をしたのか追うのが非常に困難になる。現場のコーディング標準では、「予約語として機能しうる単語は、たとえ許容されていても変数名に使用しない」のが鉄則だ。
—
EXTERNAL属性:共有変数の「落とし穴」
異なるコンパイル単位(別のソースファイル)で変数を共有したいとき、我々は `EXTERNAL` 属性を用いる。しかし、ただ宣言すればいいという甘いものではない。
リンクエディタ(Binder)は、`EXTERNAL` 属性を持つ変数名を「シンボル」として抽出し、名前が一致するものを同一のメモリ領域に割り当てる。ここで最も恐ろしいのが、「宣言の不一致」だ。
例えば、モジュールAで `DCL G_WORK_AREA CHAR(100) EXTERNAL;` とし、モジュールBで誤って `DCL G_WORK_AREA CHAR(80) EXTERNAL;` と記述したとする。コンパイラは個別のソースしか見ないため、文句は言わない。しかし実行時、モジュールBがメモリを上書きし、モジュールA側が意図しないデータを読み込むといった、悪夢のような挙動を引き起こす。
—
実践的な実装例:安全な共有データ構造
実務でこれを防ぐには、共有変数を専用の「Includeメンバー」に切り出し、 `%INCLUDE` で読み込むことだ。これにより、定義の不一致を物理的に排除できる。
以下に、VSAMアクセスを前提とした実務的なコードのひな形を示す。
/i
/ — 共有データ定義用のINCLUDEメンバー(SHARE_DS) — /
DCL 1 G_COMMON_DATA EXTERNAL,
5 G_VSAM_KEY CHAR(10), / 共通キー /
5 G_STATUS_CODE FIXED BIN(15), / 処理ステータス /
5 G_PROC_DATE CHAR(8); / 処理日付 /
/ — メイン処理モジュール — /
MAIN_PROC: PROC OPTIONS(MAIN);
%INCLUDE SHARE_DS; / 定義の単一ソース化 /
/ VSAMファイルに対する処理 /
EXEC CICS READ FILE(‘FILE01’)
RIDFLD(G_VSAM_KEY)
INTO(WORK_AREA);
/ ONユニットによる例外制御 /
ON ENDFILE(SYSIN) BEGIN;
PUT SKIP LIST(‘入力終了を検知しました’);
GO TO FINISH;
END;
/ BUILTIN関数での状態確認 /
IF VERIFY(G_VSAM_KEY, ‘0123456789’) ^= 0 THEN DO;
PUT SKIP LIST(‘キーの形式が異常です’);
SIGNAL ERROR;
END;
FINISH:
RETURN;
END MAIN_PROC;
—
現場の知恵:デバッグと保守のコツ
1. シンボルマップを確認せよ
コンパイルリストの末尾に出力される「Cross-Reference」や「Symbol Table」を必ずチェックすること。EXTERNAL変数が意図した通りにリンクされているか、オフセットを確認する癖をつけておけば、深刻なメモリ破壊は防げる。
2. ONユニットの過信は禁物
`ON ERROR` や `ON ENDFILE` は強力だが、制御フローをジャンプさせるため、多用するとどこから来たのか分からなくなる。原則として、例外処理は局所的に閉じ、グローバルな状態を `ON` ユニット内で安易に変更しないこと。
3. モジュール分割の戦略
EXTERNAL変数を多用する設計は、依存関係がスパゲッティ化する元凶だ。可能な限り、引数渡し(BY ADDR/BY VALUE)を優先し、EXTERNAL変数はあくまで「バッチ実行を通して不変なパラメータ」や「どうしても共有が必要なバッファ」に限定すべきだ。
メインフレームの保守は、技術的な正しさを追求するだけでなく、「5年後の自分が読んでも理解できるか」というコードの謙虚さが求められる世界だ。今回のEXTERNALの知識も、ぜひ安全な設計のために活用してほしい。
何か疑問があれば、いつでも質問してくれ。現場の最前線で培った知見は、出し惜しみするつもりはないからね。
