ずっと待ち望んでいたCALLの機能強化
今年は、クリスマスがずいぶん早くやって来ました。正確に言えば、5月3日のことです。サンタさん、ありがとう。IBMからのプレゼントが2つ届いたのです。1つ目は、私が長年要望していたCLの機能強化でした。2つ目は、喜ばしいことに、何年も前に私が書いたユーティリティの出番をほとんどなくしてくれる機能改善でした。どちらも、CL CALLコマンドに関連するものです。
CALLは、CLで最も使用されている、最も重要なコマンドであることは間違いないでしょうが、にもかかわらず、ここ何年もの間、手付かずにされてきました。IBMが、この重要な言語の機能強化に時間と資源を費やすのが適切だと考えていてくれたことを嬉しく思います。特に、CALLの機能強化を目にすることができて非常に嬉しく思います。
プレゼントその1: 式
まずは、私が待ち望んでいたプレゼントについて見て行きましょう。
修飾オブジェクト名とオブジェクト タイプという2つのパラメーターを受け取るCLプログラムがあるとします。修飾オブジェクト名は、20バイトの文字値です。最初の10文字はオブジェクト名で、残りの10文字はライブラリー名または*LIBLなどの特殊値です。
オブジェクトが存在することを確認したい場合は、次のようにして行うことができます。
pgm parm(&inQualObj &inObjType)
dcl &inQualObj *char 20
dcl &inObjType *char 10
ChkObj obj(%SST(&inQualObj 11 10)/%SST(&inQualObj 1 10)) +
ObjType(&inObjType) aut(*ObjExist)
OBJパラメーター内のサブストリング関数に注目してください。2つあります。OBJパラメーターでは式を指定することができる(良い考えだと私は思います)ため、オブジェクト名およびライブラリー名をスクラッチ変数へコピーする必要はありません。
では、ライブラリー名を別のプログラムに渡したいとします。IBM iおよびその前身の歴史の大半を通じて、次のように、1回使用するだけの変数を宣言する必要がありました。
pgm parm(&inQualObj &inObjType)
dcl &inQualObj *char 20
dcl &inObjType *char 10
dcl &Lib *char 10
ChgVar &Lib %SST(&inQualObj 11 10)
call pgm(X) parm(&Lib)
どうしてでしょうか。これは、CALLコマンドのPARMパラメーターで式を使用することが許容されてこなかったためです。 これには長い間悩まされました。
その後、IBMは定義済み変数を導入しました。これは1つの改善だと思います。
pgm parm(&inQualObj &inObjType)
dcl &inQualObj *char 20
dcl &inObjType *char 10
dcl &Lib *char 10 stg(*defined) DefVar(&inQualObj 11)
call pgm(X) parm(&Lib)
メモリーで&Libは&inQualObjをオーバーレイするため、CHGVARコマンドは必要ありません。一方を変更すると、他方も変更されます。
しかし、ついに、PARMパラメーターで式を使用できるようになりました。
pgm parm(&inQualObj &inObjType)
dcl &inQualObj *char 20
dcl &inObjType *char 10
call pgm(X) parm(%SST(&inQualObj 11 10))
考えてみてください。プログラムにパラメーターを渡すだけのために、1回使用するだけの変数の宣言は、もう必要ないのです。素晴らしいことです。
以下は、論理値を使用する例です。
ジョブがロギング ファイルを作成する必要があるかどうかを示す、*YESまたは*NOという4バイトの文字値を受け取るCLプログラムがあるとします。この値は、別のプログラムに渡すときには、「1」または「0」に変換する必要があります。これまでは、次のようにする必要がありました。
pgm parm(&CrtLog)
dcl &CrtLog *char 4
dcl &CrtLogLgl *lgl
chgvar &CrtLogLgl (%upper(&CrtLog) *eq *YES)
call pgm(X) PARM(&CrtLogLgl)
新しいやり方はこうです。
pgm parm(&CrtLog)
dcl &CrtLog *char 4
call pgm(X) PARM(((%upper(&CRTLOG) *EQ *YES) (*LGL 1)))
以下は、連結を伴う例です。
call pgm(x) (%sst(&ItemType 5 1) *cat %sst(&Option 2 1))
まだまだあります。これについて網羅的にテストしているわけではありませんが、CHGVARコマンドのVALUEパラメーターに入れることができるものは何でも、PARMで使用できると考えてよさそうです。
プレゼントその2: パラメーター定義
IBMからの2つ目のプレゼントがどれほど素晴らしいものなのかを理解するために、まず、CLがこの世界へ導入されて以来、どのように動作してきたかについて確認してみましょう。
はるか昔、System/38向けにCLを作成した開発者は、いくつかの決断を迫られました。彼らが直面した1つの問題は、CALLコマンドでのリテラル パラメーターの使用法に関するものでした。すなわち、そのようなパラメーターがメモリーでどのように格納されるべきであるかということです。彼らは慣例に従いましたが、これについては、何度も記事で取り上げられています(その一例がこちらの記事です)。
- 32バイト以下の文字リテラルは、32バイトのメモリーで格納される。リテラルが32バイトに満たない長さの場合、システムは末尾ブランクを埋め込む。
- 32バイトを超える文字リテラルは、最後の非ブランク文字までを含めて、すべての文字を格納するのに必要なバイト数で格納される。言い換えれば、末尾ブランクはメモリーに格納されない。
- 数値リテラルは、想定小数部桁数5桁の15桁のパック10進数として渡される。
プログラマーは、これらの制約に対処する必要がありました。よく行われる対処法には以下のものがあります。
- 呼び出される側のプログラムで、すべてのパラメーターを文字として定義する。
- 文字リテラルに余分な非ブランク文字を付加する。
- パラメーターを通じてではなく、ローカル データ域を通じて、プログラムにデータを渡す。
- 呼び出される側のプログラムで、数値パラメーターを15,5として宣言する。
- 数値を16進数リテラルとして渡す。
- 呼び出される側のプログラム向けにコマンド インターフェースを作成する。
これらはもう必要ありません。リテラル値がどのように格納されるようにしたいかを、システムに伝えることができるようになりました。
次の例では、リテラル「302」は、5桁のパック10進数値としてプログラムXに渡されます。
CALL PGM(X) PARM((302 (*DEC 5 0)))
変数を使用しているところでも、システムはリテラル値とみなすかもしれないことに留意してください。システムは、CLプログラム/プロシージャー内での変数の定義については把握しています。そうでなければ、CALLが解釈されます。良い例が、ジョブ投入(SBMJOB)コマンドです。
dcl &CusNbr *dec 5
SbmJob cmd(CALL PGM(X) PARM((&CUSNBR))) job(Whatever)
ジョブ投入コマンドは、要求メッセージを作成します。これを新しいジョブが受け取り、コマンドとして実行します。新しいジョブは、サブミットする側のプログラムで&CusNbrが5桁のパックとして定義されたことを知りません。代わりに、顧客アカウント番号を15,5として定義されているものとして処理します。プログラムXが顧客番号パラメーターを5,0パックとして定義している場合、プログラムはデータ10進数エラーで異常終了します。
代わりに、このプログラムは、顧客番号を5桁のパック10進数としてプログラムXに渡す必要があります。以下に、そのやり方を示します。
dcl &CusNbr *dec 5
SbmJob cmd(CALL PGM(X) PARM((&CUSNBR (*DEC 5 0)))) +
job(Whatever)
プログラムXが顧客番号を7桁のパック10進数として定義している場合は、次のようにして、この番号を適切にフォーマットし直すことができます。
dcl &CusNbr *dec 5
SbmJob cmd(CALL PGM(X) PARM((&CUSNBR (*DEC 7 0)))) +
job(Whatever)
今回の新たな機能強化によって、私が何年も前に書いて、それ以来ずっと使用してきたユーティリティの出番はほとんどなくなりそうです。つまり、私が書いた RUNコマンドのことです。このユーティリティは、作業を行うすべてのシステムで結局はインストールしているように思います。おそらく今後は、必要になる頻度は少なくなるでしょう。
こうしたCALLの新たな挙動のおかげで、RUNの出番は ほとんど なくなると述べたのには理由があります。それは、行われる必要があることをCALLが行わないケースに出くわしたからでした。以下は、前段落にリンクを示した記事から引用したコマンドです。
RUN PGM(MYPGM) PARM(('I like cheese a whole lot!' *CHAR (64)) +
(3.14 *DEC (7 2)) +
(12345678901 *DEC (11)))
このコードをCALLに変換しました。
call pgm(MYPGM) PARM(('I like cheese a whole lot!' (*CHAR 64)) +
(3.14 (*DEC 7 2)) +
(12345678901 (*DEC 11 0)))
どうなるでしょうか。これはコンパイルされません。コンパイラーは、3つ目のパラメーターを有効数字11桁で定義することは許容しますが、11桁のリテラルを渡すことは許容しません。今すぐにはRUNを完全に引退させるつもりはありませんが、おそらく、これまでほど使用する必要はなくなるように思います。
お手本はアインシュタイン
アルベルト・アインシュタインはこう述べています。「ものごとはできるかぎりシンプルにすべきだ。しかし、シンプルすぎてもいけない。」 私はこの考えに同感です。そうする必要があるときは、私は複雑なソース コードを書きますが、そうするのは、よりシンプルなコードでは目的にかなわないというときだけです。
【 あわせて読みたい記事 】