定数の畳み込みを気に掛けるべき理由
定数の畳み込みは、コンパイラー最適化技法の1つであり、定数が含まれている演算を、可能であればコンパイラーが結果値に置き換えるという技法です。定数の畳み込みは最新のコンパイラーでは一般的であり、RPGリファレンスによれば、RPGコンパイラーはこの技法を使用しています(たとえば、 %div および %rem 関数についてのドキュメントを参照)。(訳注:定数畳み込み)
しかし、読者の皆さんも私も、コンパイラーを書くことはありません。書くのは、ビジネス アプリケーションです。では、どうして、定数の畳み込みを気に掛ける必要があるのでしょうか。これは、じっくり考えてみる価値のある問いです。
「暗黒時代」には、どのようにRPGを書く必要があったかを考えてみましょう。
C MOVE CUSTNR CUSTSV 50
ここでは、顧客アカウント番号を保管フィールドへコピーしています。おそらく、コントロール ブレークをチェックするためです。このコードではどのような問題が起こり得るでしょうか。単にこういうことです。すなわち、誰かが顧客アカウント番号フィールドのサイズを増やした場合に、CUSTSVは適正値へセットされなくなります。さらに悪いことには、顧客に100000以上のアカウント番号が割り当てられるまで、エラーは表面化しません。そして、そうなるのは、プログラム変更の後、かなり時間が経ってからのことかもしれません。
そのため、IBMの優秀なスタッフたちは、代わりに、以下のようにするよう促しました。
C *LIKE DEFN CUSTNR CUSTSV
C MOVE CUSTNR CUSTSV
エラーの源となり得るものが1つ取り除かれています。当時、私が作業していたRPGコンパイラーに、IBMが*LIKE DEFN命令コードを追加したとき、私は大喜びしたものです。
今日では、定義仕様書で同じことを行います。
dcl-s Cust_Account_Save like(Cust_Account);
あるいは、少なくともそうするべきです。プログラマーがLIKEキーワードを使用できたのにそうしなかったコードを数多く見てきましたが、それは古いコードについての話ではありません。
LIKEキーワードには、定義されている変数のサイズを増減させる2つ目の引数があります。たとえば、
dcl-s Limit packed (7: 2);
dcl-s NewLimit like(Limit: +2);
NewLimitは9桁の長さとなるように定義されます。そのうちの2桁は小数部の桁です。
また、IBMは、データ定義についての情報を取得する3つの組み込み関数も提供しています。すなわち、%SIZE、%LEN、および%DECPOSです。これらの素晴らしい関数は、コンパイラーが定数の畳み込みを使用するさらに多くの可能性を生み出します。 LIKEキーワードと同様に、それらの関数は十分に活用されておらず、真価が認められていないようです。
たとえば、文字ストリングから10進数を取得する必要があるとします。%DEC関数は、簡単にそのようなタスクを実行しますが、結果の値の定義方法を知っている必要があります。このようにすることができます。
dcl-s Limit packed (7: 2);
Limit = %dec (TextValue: 7: 2);
しかし、こうした方がより適切です。
dcl-s Limit packed (7: 2);
dcl-c Length const( %Len (Limit) );
dcl-c DecPos const( %DecPos (Limit) );
Limit = %dec (TextValue: Length: DecPos);
仮にLIMITの定義が変更された場合でも、このコードはその変更に楽々と対応できます。
%LENおよび%DECPOS関数呼び出しを、直接、%DEC関数に入れることができないというのは、私には奇妙に思われますが、そうなっているようです。代わりに、2つの名前付き定数を宣言するという、遠回りの方法を使用する必要があります。こうした挙動については「 RPGリファレンス」で説明されているとしても、これは難しいやり方だと思います。明らかに、マニュアルを読むことよりも、もっと大事な行うべきことがあるからです。
また、これらの関数は、データ タイプを変更する際にも重宝します。たとえば、固定長の文字フィールドと同じ量のデータを格納するvarchar変数が必要だとします。LIKEキーワードを使用することはできませんが、以下のようにすることができます。
dcl-s Address char(20);
dcl-s AddrMod varchar(%size(Address));
一部のコンパイラーは、定数の使用に関して、RPGコンパイラーに比べてより寛容です。たとえば、変数をデータベース フィールドの2倍の長さにする必要があるとします。おそらく、その変数にそのフィールドの16進表現を格納するかもしれませんし、それぞれのアポストロフィーを2つのアポストロフィーに置き換えるかもしません。一部のコンパイラーでは、次のようなものも許容されるでしょう。
dcl-s Address char(20);
dcl-s AddrMod char(%len(Address)* 2);
RPGコンパイラーはそうではありません。代わりに、別の方法を見つける必要があります。たとえば次のような方法ですが、私にはその場しのぎの解決策に思えます。
dcl-s Address char(20);
dcl-ds AddrMod;
*n like(Address);
*n like(Address);
end-ds AddrMod;
LIKEキーワードやデータ定義組み込み関数をより多く使用できればできるほど、コンパイラーが定数の畳み込みを使用する機会が増えることになります。しかし、どうして、定数の畳み込みを気に掛ける必要があるのでしょうか。最適化されたオブジェクト コードである必要があるからでしょうか。これは、もっともな理由ではありますが、さらによりもっともな理由があります。すなわち、定数の畳み込みが行われていることにより、プログラムを修正したときに、プログラムがブレークする可能性が低くなるからです。皆さんにも私にも、バグを探すことよりも、もっと大事な行うべきことがあるのです。