おい、最近の若手はCOBOLから入ることが多いから、いざPL/Iの保守現場に放り込まれると「なんじゃこりゃ」と頭を抱えることが多いらしいな。特に、構造体を丸ごとファイルに吐き出したり、逆に電文をごっそり受け取ったりする処理に直面したときだ。
「あれ? COBOLみたいに `MOVE CORRESPONDING` や `REDEFINES` みたいな小細工をしなくても、なんでこの先輩のコードは構造体をそのまま1本の文字列として扱えているんだ?」ってな。
いいか、よく聞いてくれ。PL/IにはCOBOLのような「予約語の縛りによるガチガチの構文規則」とは一味違う、独特の懐の深さ――いや、一歩間違えると大怪我をする「強力すぎる機能」がある。その代表格が、今回解説する `STRING` ビルトイン関数 による構造体から文字列へのキャストだ。
今日のテーマは、この `STRING` 関数がメモリ上で何をやっているのか、そしてレガシーシステムの現場でどう安全に使いこなすべきか、俺が骨の髄まで叩き込んでやる。
—
1. 予約語を持たないPL/Iの懐の深さと、今回の核心
まず大前提として、PL/IにはC言語やCOBOLのような「絶対に変数名に使ってはいけない予約語(Reserved Words)」が、実は文脈依存を除いてほぼ存在しない。極端な話、`IF` や `READ` さえもプログラマが変数名として定義できてしまう(コンパイラは前後の文脈から判断するが、そんな狂った真似は絶対にやっちゃいけないぞ)。
この「自由度の高さ」は、データ構造の定義においても猛威を振るう。特に、親子関係を持つ構造体(Structure)を定義したとき、PL/Iコンパイラはそれをメモリ上でどう配置しているか知っているか?
通常、構造体のメンバーは宣言順にメモリ上に並ぶが、データ型の境界調整(アライメント、例えば半ワードや全ワード境界へのパディング)が挟まることがある。この「パディングの罠」を無視して構造体をまるっとバイト列として扱おうもんなら、夜間バッチで突如としてデータ化けを起こし、運用担当者から血の涙を流す電話がかかってくることになる。
そこで登場するのが、`STRING` ビルトイン関数だ。これを使えば、ネストした複雑な構造体であっても、一瞬で「ただの連続した文字の塊(CHARACTER)」にキャストできる。
—
2. STRING関数によるメモリ上の連続性確保とアドレス変換
`STRING(構造体名)` を記述すると、コンパイラは何をしているのか?
内部的には、コンパイラは指定された構造体(およびその下位のサブ構造体)が占有するメモリ領域の先頭アドレスと全長(バイト数)を算出し、それをあたかもひとつの `CHARACTER VARYING` または固定長の `CHARACTER` 文字列であるかのように再解釈(Re-interpretation)させるコードを生成する。
ただし、ここでベテランとしての注意点を一つ。
構造体の中に不連続なポインタ変数や、余計なアライメント調整によるパディングが含まれている場合、`STRING` で展開した文字列の長さは、単純な各メンバーの `LENGTH` の単純合計とは一致しないことがある。
だからこそ、VSAMファイルへのレコード出力や、TCP/IPソケット経由での電文送受信といった「バイナリレベルの厳密さが求められる処理」では、構造体の定義に `UNALIGNED`(アンアライメント)属性 を明示することが鉄則だ。これをつけることで、コンパイラに「パディングなしで隙間なくメモリを詰めろ」と指示できる。
—
3. 実践!VSAMファイル入出力とONユニットを伴う堅牢なコード例
口で言うだけじゃ信用しないだろうからな。実際のメインフレームの夜間バッチを想定した、実用的なPL/Iのソースコードを見せてやろう。
このサンプルは、顧客マスタの構造体を `STRING` 関数で文字列表現にキャストし、VSAMのKSDS(キー順データセット)へ出力する処理だ。さらに、万が一の入出力エラーに備えて `ON` ユニットによる例外処理も組み込んでいる。
1
—————————————————————-
- 顧客マスタ構造体をSTRING関数で文字列化しVSAMへ書き込むサンプルバッチ
—————————————————————-
CUST_BCH: PROC OPTIONS(MAIN);
DCL 1 CUST_REC UNALIGNED, / 隙間なくメモリに配置 /
5 CUST_ID CHAR(5), / 顧客ID /
5 CUST_NAME CHAR(20), / 顧客名 /
5 CUST_INFO UNALIGNED,
10 REG_DATE CHAR(8), / 登録日 (YYYYMMDD) /
10 STATUS CHAR(1), / ステータス (0:無効 1:有効) /
5 BALANCE PIC ‘99999999V99’ BIN; / 残高 (バイナリ数値) /
DCL OUT_AREA CHAR(36); / 構造体トータル長と一致させる /
DCL VSAM_FILE FILE RECORD OUTPUT;
DCL IO_ERR_FLAG BIT(1) INIT(‘0’B);
————————————————————
- ONユニットによるファイル入出力異常の捕捉
————————————————————
ON ERROR
BEGIN;
DISPLAY(‘【SEVERE ERROR】予期せぬ例外が発生しました。処理を中断します。’);
IO_ERR_FLAG = ‘1’B;
GOTO ABEND_ROUTINE;
END;
ON ENDFILE(VSAM_FILE)
BEGIN;
DISPLAY(‘ファイル終端に到達しました。’);
END;
- ファイルオープン
OPEN FILE(VSAM_FILE) ENVIRONMENT(WBAM); / WBAM等は環境に応じる /
- データの初期設定
CUST_ID = ‘A0012’;
CUST_NAME = ‘山田 太郎 ‘;
REG_DATE = ‘20231025’;
STATUS = ‘1’;
BALANCE = 1250000; / バイナリ値の設定 /
————————————————————
- [核心] STRING関数による構造体の文字列キャスト
- 構造体全体を一つの文字変数(OUT_AREA)に一括転送する
————————————————————
OUT_AREA = STRING(CUST_REC);
- VSAMファイルへレコード出力
WRITE FILE(VSAM_FILE) FROM(OUT_AREA);
IF IO_ERR_FLAG THEN
GOTO ABEND_ROUTINE;
DISPLAY(‘正常終了:顧客ID = ‘ || CUST_ID);
CLOSE FILE(VSAM_FILE);
RETURN;
ABEND_ROUTINE:
DISPLAY(‘異常終了ルーチンを通過しました。シスログを確認してください。’);
SIGNAL ERROR;
END CUST_BCH;
コードの解説と現場のツボ
1. `UNALIGNED` の指定
構造体の定義に `UNALIGNED` をつけている点に注目しろ。これを忘れると、コンパイラが勝手に境界調整用のパディングバイトを挿入し、`STRING(CUST_REC)` で取り出される文字列のサイズが想定(36バイト)より大きくなってしまい、VSAM側のレコード長エラー(FILE STATUSの不一致など)を引き起こす。
2. `STRING(CUST_REC)` の威力
個別のフィールドを一つずつ連結しなくても、この1行だけで階層構造を持ったレコード全体がフラットな `OUT_AREA` に変換される。電文のログ出力や、ダンプ採取時のメモリイメージ作成にはこれ以上ないほど強力だ。
3. `ON` ユニットの活用
メインフレーム開発ではお馴染みの `ON ERROR` や `ON ENDFILE` だ。PL/Iはエラーが発生した際、C言語のように毎回戻り値のステータスコードを愚直にチェックするだけでなく、言語処理系レベルで例外をトラップして制御フローを安全に切り替えることができる。
—
4. 先輩からの現場アドバイス:デバッグと移行時の注意点
最後に、今後マイグレーションや大規模改修でこの手のコードを触る際の「生きた知見」を授けておく。
- 文字コード(EBCDIC vs ASCII)の壁に気をつけろ
もし将来的にメインフレームからオープン系(LinuxやWindows)へPL/Iごと移行する、あるいはデータを連携する際、`STRING` でキャストしたバイナリ文字列の中に `FIXED BIN` や `PIC` の数値データが混ざっていると、EBCDICとASCIIの文字コード差異だけでなく、バイトオーダー(エンディアン)の壁にブチ当たる。ビッグエンディアン前提で組まれたバイナリキャストは、リトルエンディアン環境ではそのままでは動かない。
- ダンプを読むときはレイアウトマップを手元に
スナップダンプやSYSUDUMPを解析する際、`STRING` で固められたエリアがメモリ上でどう並んでいるか、コンパイルリストの「Storage Map(ストレージ・マップ)」を必ず突き合わせろ。オフセットが何バイトズレているか一発でわかるはずだ。
PL/Iの `STRING` 関数は、正しく使えばこれほどエレガントで強力な武器はない。しかし、一歩間違えばメモリ破壊という魔物を呼び起こす両刃の剣だ。構造体のレイアウトとメモリの連続性を頭に叩き込み、自信を持ってコードを書けるようになってくれ。期待しているぞ。
