現場のPL/I職人が教える:RETURNS属性による関数設計の「落とし穴」と鉄則
メインフレームの現場で長くコードを読んでいると、「なぜか本番バッチでデータが化ける」「原因不明の例外終了(ABEND)が発生する」といったトラブルの多くが、実はプロシージャのインターフェース設計、特に`RETURNS`属性の不整合に起因していることに気づかされます。
今回は、PL/Iにおいて関数型プロシージャを定義する際の「戻り値定義」について、実務的な観点から深掘りします。
—
1. なぜ「RETURNS」の整合性が命取りになるのか
PL/Iの強力な点は、コンパイラが型チェックを厳格に行うことですが、一方で暗黙的な型変換(デフォルトの変換規則)が災いすることもあります。特に古いソースコードでは、`RETURNS`を省略したり、呼び出し側と戻り値の型が微妙にずれているケースが見受けられます。
例えば、`FIXED BIN(31)`で計算した結果を`FIXED DEC(15,0)`で受け取ろうとした場合、あるいはその逆。これらはコンパイラが気を利かせて変換してくれますが、オーバーフローや精度落ちは容赦なく発生します。
2. 実践コード:堅牢な関数型プロシージャの書き方
まずは、VSAMファイルから特定レコードのステータスを読み取り、計算結果を返す標準的な構成例を見てみましょう。
1
/ —————————————————————— /
/ FUNCTION: GET_VALUATION /
/ 戻り値: 評価額 (FIXED DEC(15, 2)) /
/ —————————————————————— /
GET_VALUATION: PROCEDURE(IN_ID)
RETURNS(FIXED DEC(15, 2)) OPTIONS(REENTRANT);
DCL IN_ID CHAR(10);
DCL WRK_VAL FIXED DEC(15, 2) INIT(0);
/ VSAMアクセス用のエリア定義 /
DCL VSAM_RECORD CHAR(100) BASED(ADDR(VSAM_BUF));
/ ビルトイン関数を利用した安全な比較 /
IF VERIFY(IN_ID, ‘0123456789’) ^= 0 THEN DO;
SIGNAL ERROR; / 入力異常をONユニットへ飛ばす /
END;
/ ここに本来のVSAM読み込みロジックが入る /
/ READ FILE(MSTFILE) INTO(VSAM_RECORD) KEY(IN_ID); /
WRK_VAL = 12345.67; / 擬似的な計算結果代入 /
RETURN(WRK_VAL);
END GET_VALUATION;
ここで意識すべき「現場の鉄則」
- OPTIONS(REENTRANT)の付与: 大規模バッチでは、マルチタスクや再帰呼び出しを想定して必ず付与しましょう。これを忘れると、スタティックストレージの競合で地獄を見ることになります。
- 明示的なRETURNS定義: 呼び出し側(CALL側)で`DCL`にて`RETURNS`属性を正確に宣言しておくことが必須です。これを怠ると、コンパイラは戻り値を`FIXED BIN(15)`と勝手に解釈し、データが切り捨てられるという悲劇を招きます。
—
3. トラブルを未然に防ぐ「呼び出し側」の心得
呼び出し側では、戻り値を受け取るための`DCL`が鍵を握ります。
1
MAIN_PROC: PROCEDURE OPTIONS(MAIN);
/ 外部関数プロトタイプの宣言 (ここが最も重要!) /
DCL GET_VALUATION ENTRY(CHAR(10))
RETURNS(FIXED DEC(15, 2));
DCL RESULT_VAL FIXED DEC(15, 2);
DCL ERR_FLG FIXED BIN(15) INIT(0);
/ ONユニットによるエラーハンドリング /
ON ERROR BEGIN;
PUT SKIP LIST(‘関数呼び出しで異常が発生しました’);
ERR_FLG = 1;
END;
/ 関数呼び出し /
RESULT_VAL = GET_VALUATION(‘1234567890’);
IF ERR_FLG = 0 THEN
PUT SKIP EDIT(‘評価額は:’, RESULT_VAL)(A, F(15,2));
END MAIN_PROC;
4. ベテランからのアドバイス:デバッグの極意
もし、本番で「なぜか戻り値が正しくない」という事象に遭遇したら、以下の手順で切り分けてください。
1. コンパイラ・リスト(マッピング)の確認: `ATTRIBUTES`オプションをつけてコンパイルし、戻り値の型が意図通りか、暗黙変換が発生していないかを確認する。
2. ON ERRORのトレース: `SIGNAL ERROR`を活用し、異常系フローを意図的に通して、`ON`ユニットが正しくスタックを解放できているか確認する。
3. ストレージ・ダンプの比較: `HEX`関数を使用して、関数から戻った直後のレジスタ/メモリ上の値を直接確認する。PL/Iはメモリ配置が明快なので、ダンプを読めば「型」の違いは一目瞭然です。
—
まとめ
PL/Iの`RETURNS`属性は、単なる値の受け渡し以上の意味を持ちます。それは「システム間の契約」です。呼び出し側と被呼び出し側で、型定義という名の契約書をしっかり交わすこと。これが、バッチ改修における障害をゼロに近づける唯一の方法です。
皆さんの現場のソースコードも、一度`DCL`の宣言部を再確認してみてください。長年気づかなかった「型不整合の爆弾」が眠っているかもしれませんよ。
次回は、「VSAMのキー指定におけるパディング問題」についてお話しします。それでは、良いバッチ・プログラミングを。
