【実務・中級編】ENTRY属性と引数渡し(BYADDR/BYVALUE)の規約 – PL/Iの基本構文とデータ制御実践ガイド

おい、新人。今日も元気にVSAMのオープンエラーと格闘しているか?
「先輩、なんだかC言語から呼び出したPL/Iのサブプログラムで、引数の値が変なんです……」だって?

まったく、これだからメインフレームの深淵を覗いたことがない奴は可愛い。お前が今直面しているそのバグ、十中八九 `BYADDR` と `BYVALUE` の規約違い、あるいはPL/I特有の「予約語を持たない」という仕様のせいでコンパイラが意図を誤解したかのどちらかだ。

今日はな、PL/Iのサブルーチン呼び出しにおける引数受け渡し(ENTRY属性)と、COBOLやC言語といった他言語連携時の「絶対に踏んではいけない地雷」について、この俺が徹底的に叩き込んでやる。耳の穴をかっぽじってよく聞け。

—

1. PL/Iの識別子と「予約語がない」という変態的な仕様

まず大前提として、お前らが普段使っているJavaやC、あるいはCOBOLには「予約語(Reserved Words)」というものが存在する。`IF` とか `THEN` とか、変数名に使ったらコンパイラに怒られる言葉だ。

だがな、PL/Iには厳密な意味での予約語というものが存在しない。

「じゃあ、`IF` っていう名前の変数を作れるんですか?」
答えは YES だ。PL/Iコンパイラは上下文依存(Context-sensitive)でコードを解釈する。つまり、「そこにそれが書かれていたらキーワード、それ以外ならただの識別子(変数名)」として処理しやがる。

これが何を意味するか分かるか?
もしお前がうっかり `IF = 1;` なんてコードを書こうものなら、コンパイラは文脈からそれを解釈しようとして、とんでもない構文エラーを吐くか、最悪の場合、意図しないコードを生成する。他言語連携、特にC言語のヘッダファイルやCOBOLのデータ部から自動生成したインクルード体をそのまま取り込むときには、この「予約語がない」仕様が原因で、名前の衝突やパースミスを引き起こすことがある。常に頭の片隅に置いておけよ。

—

2. ENTRY属性と引数渡しの基本:BYADDR と BYVALUE

さて、本題の引数渡しだ。
PL/Iのデフォルトの引数受け渡し規約は、C言語や現代の多くの言語とは根本的に異なる。

  • `BYADDR`(デフォルト):

引数のアドレス(ポインタ)を渡す。呼び出し先では、渡された領域を直接参照・更新する(参照渡しに近い)。

  • `BYVALUE`:

引数の値そのものをコピーして渡す。呼び出し先で値を書き換えても、呼び出し元には影響しない(値渡し)。

メインフレームの基幹系で動くCOBOLや、古くからあるPL/Iモジュール同士の連携であれば、基本は `BYADDR` だ。しかし、C言語(LE: Language Environment下でのC/C++)とリンクする際や、OSのAPIを叩く際には、この規約を明示的に指定しないと、レジスタの不整合を起こして一発で S0C4(保護例外) の御ミドルにお目にかかることになる。

他言語連携時の鉄則

1. C言語からPL/Iを呼ぶ場合: Cはデフォルトが値渡し(`BYVALUE`)だ。したがって、PL/I側の `ENTRY` または `PROCEDURE` の引数には必ず `BYVALUE` を明示しろ。さもないと、Cから渡された「値」をPL/Iが「アドレス」と勘違いし、メモリの変な場所を指し示して即死する。
2. PL/IからCOBOLを呼ぶ場合: COBOLは基本的にアドレス渡しだ。PL/I側で `BYADDR`(省略時のデフォルト)で渡せば大体うまくいくが、記述ミスでポインタのポインタを渡したりしないよう、`DESCRIPTOR` やアライメント(BOUND/UNBOUND)に気をつけろ。

—

3. 実践!VSAMファイル処理と外部サブルーチン連携のコード例

口で言うだけでは信用しないだろうからな。実際に、VSAM(KSDS)からレコードを読み込み、それをC言語風の規約(`BYVALUE`)を意識した外部サブルーチンに渡して処理する、メインフレームの現場でよくあるバッチプログラムの骨格を見せてやろう。

1
/ /
/ プログラム名: MASTL010 /
/ 概要: VSAM(KSDS)ファイルを順編成で読み込み、外部ルーチンに /
/ データを渡して加工・出力するバッチメインプログラム /
/ /
MASTL010: PROC OPTIONS(MAIN);

DCL MSTR-FILE FILE RECORD SEQUENTIAL INPUT;

/ VSAMレコード様定義(マクロ展開を想定した構造体) /
DCL 1 MSTR-REC,
3 MSTR-KEY CHAR(8),
3 MSTR-NAME CHAR(30),
3 MSTR-AMT FIXED DEC(9,2);

DCL EOF-FLAG CHAR(1) INIT(‘0’);

/ 外部サブプログラム(C言語連携を想定しBYVALUEを指定した例) /
DCL PROC_DATA ENTRY(CHAR(8) BYVALUE, FIXED DEC(9,2) BYVALUE)
RETURNS(FIXED BIN(31)) EXTERNAL(‘PROCDATA’);

DCL RET-CODE FIXED BIN(31) INIT(0);

/ ONユニットによる入出力エラーのグローバル制御 /
ON ENDFILE(MSTR-FILE) EOF-FLAG = ‘1’;
ON ERROR BEGIN;
PUT SKIP EDIT (‘ 致命的エラー発生: 異常終了します ‘) (A);
SIGNAL FINISH;
END;

/ VSAMファイルのオープン /
OPEN FILE(MSTR-FILE);

PUT SKIP EDIT (‘— メイン処理開始 —‘) (A);

/ メインループ /
DO WHILE (EOF-FLAG = ‘0’);

/ レコード読み込み (READステートメント) /
READ FILE(MSTR-FILE) INTO(MSTR-REC);

IF EOF-FLAG = ‘0’ THEN DO;
/ 組み込み関数(BUILTIN)を活用したデータのトリムと確認 /
PUT SKIP EDIT (‘READ KEY: ‘, MSTR-KEY, ‘ NAME: ‘, TRIM(MSTR-NAME))
(A, A, A, A);

/ 外部ルーチン呼び出し(BYVALUEによる値渡し) /
RET-CODE = PROC_DATA(MSTR-KEY, MSTR-AMT);

IF RET-CODE ^= 0 THEN DO;
PUT SKIP EDIT (‘サブプログラムでエラー検出 RC=’, RET-CODE) (A, F(4));
END;
END;

END;

/ クローズ処理 /
CLOSE FILE(MSTR-FILE);

PUT SKIP EDIT (‘— メイン処理正常終了 —‘) (A);
RETURN;

END MASTL010;

—

4. このコードのポイントと現場の勘所

お前、上のコードを見て「ふーん」で終わらせてないだろうな? 重要なポイントをいくつか解説しておくぞ。

1. `ON ENDFILE` と `ON ERROR` の制御フロー
レガシーシステムの命は「異常時の振る舞い」だ。`ON ENDFILE` でフラグを立ててループを抜けるお作法はPL/Iの基本中の基本。また、`ON ERROR` でキャッチして `SIGNAL FINISH;` に持ち込むことで、ダンプリストを汚さずにキレイにABENDを制御できる。この辺りの例外処理をサボると、運用保守のオペレーターから夜中に叩き起こされることになるから覚えておけ。
2. `TRIM` 組み込み関数(BUILTIN)の活用
PL/Iの `CHAR` 型は固定長だ。VSAMから読み込んだパディングスペースだらけの文字列をそのままログに出すと見づらい。`TRIM` などのBUILTIN関数を適切に使うことで、モダンな文字列操作が可能になる。コンパイラが自動でインライン展開してくれることが多いので、パフォーマンス面でも有利だ。
3. 外部ルーチン `PROC_DATA` の宣言
`EXTERNAL(‘PROCDATA’)` と指定することで、リンクエディタ(binder)がバインド時に別モジュールと結びつけてくれる。ここで引数に `BYVALUE` を指定しているのは、連携先のC言語側が値渡しを期待しているからだ。もしここを `BYADDR` のままでC言語の関数を叩いたら、C側は「アドレス」ではなく「ゴミが入ったレジスタの値」をポインタとしてデリファレンスし、光速でシステムダウンを引き起こす。

—

5. おわりに

先輩風を吹かせるわけじゃないが、PL/Iという言語は、言語仕様の奥が非常に深い。COBOLのように「誰が書いても同じようなコードになる」言語とは違い、書き手の技量によってエレガントにも、二度と触りたくないスパゲッティコードにもなり得る。

特に今回解説した `ENTRY` 属性、引数の渡し方(`BYADDR` / `BYVALUE`)、そして識別子のスコープや予約語なき世界観を理解していないと、マイグレーションや他言語連携の案件で痛い目を見る。

次にコードを書くときは、「なぜこの引数はこの渡し方でなければならないのか」をコンパイラの気持ちになって考えてみるんだな。期待しているぞ、新人。さて、コーヒーでも飲んで次のタスクに取り掛かるとしようか。

タイトルとURLをコピーしました