プロシージャー主導型RPGでは、Options(*Exact)を使用して厳密に
V7R2 TR1およびV7R3 TR7でRPG言語に導入されたOptions(*Exact)は、RPGプログラマーがより防御的なコーディング スタイルを採用することを可能にします。防御的なコーディングは、すべての開発者が実践しているべきコーディング手法です。
免責事項:このテスト プログラムでは、キノコの可食性について取り扱われていますが、これは、あくまでプログラムの動作の仕組みを分かりやすく説明するためのものです。キノコの可食性については、ミズーリ州自然保護局(MDC)発行の「 A Guide to Missouri's Edible and Poisonous Mushrooms(ミズーリ州の食べられるキノコと毒キノコのガイドブック)」を参考にしましたが、実際に野生のキノコ類を採取したり摂取したりする際は、事前にご自身で十分に調べて、安全性が確保されるようにしてください。
**Free
Ctl-Opt Main(Test_Exact_Options);
Ctl-Opt Debug Option(*SrcStmt:*NoDebugIO);
Ctl-Opt ActGrp(*Caller);
Dcl-Proc Test_Exact_Options;
Dcl-s answer Char(1) Inz('');
Dcl-s fry_it_up Ind;
Dcl-s mushroom_name Char(30) Inz('Chicken of the Woods False');
fry_it_up = is_edible_mushroom(mushroom_name);
If fry_it_up;
Dsply 'Fry it up!' '' answer;
Else;
Dsply 'Don''t fry it up!' '' answer;
Endif;
On-Exit;
End-Proc Test_Exact_Options;
上の例は、プロシージャーを呼び出して、その種類のキノコが食べられるかどうかを報告する単純な リニア メインRPGプログラム です。ここでは、変数mushroom_nameの長さは最大30文字であり、「Chicken of the Woods False」に初期化されることに注目しておいてください。(Chicken of the Woods:鱒茸)
is_edible_mushroomプロシージャーは、次のようになります。
Dcl-Pi is_edible_mushroom Ind;
p_mushroom_name Char(20);
End-Pi;
Dcl-s abnormal_End Ind Inz(*Off);
If p_mushroom_name in %List('Chanterelle':'Chicken of the Woods':
'Hen of the Woods':'Morel');
Return *On;
Else;
Return *Off;
Endif;
On-Exit;
End-Proc is_edible_mushroom;
なるほど、これは単純なプロシージャーです。渡された値が%List BIFに含まれている場合はtrueを返します。これはそのキノコが食べられることを意味します。そうでない場合はfalseを返し、そのキノコは食べられないことを意味します。「Chicken of the Woods False」が渡されたものの、プロシージャー インターフェースでは、p_mushroom_nameパラメーターの長さは最大で20文字のみと宣言していたことに注目してください。そのため、このプログラムを呼び出すと、受け取られる値は「Chicken of the Woods」となり、「False」という語句は脱落してしまいます。そうすると、このプログラムは、「Fry it up!(炒めて食べられる)」という誤った情報をユーザーに伝えてしまうことになります。
では、次のように、is_edible_mushroomのプロシージャー インターフェースを、Options(*Exact)が含まれるように変更するとどうなるでしょうか。
Dcl-Proc is_edible_mushroom;
Dcl-Pi is_edible_mushroom Ind;
p_mushroom_name Char(20) Options(*Exact);
End-Pi;
Dcl-s abnormal_End Ind Inz(*Off);
If p_mushroom_name in %List('Chanterelle':'Chicken of the Woods':
'Hen of the Woods':'Morel');
Return *On;
Else;
Return *Off;
Endif;
On-Exit;
End-Proc is_edible_mushroom;
プログラムを再コンパイルすると、コンパイラーは、RNF3249エラー「The type or attributes of the parameter 1 do not meet the OPTIONS(*EXACT) requirements. Reason: LEN. (30)(パラメーター1のタイプまたは属性が、OPTIONS(*EXACT)の要件を満たしません。理由: LEN. (30))」をスローします。パラメーターでOptions(*Exact)を含めることによって、より防御的なコーディング スタイルでこのプロシージャーを書くことができました。これで良いでしょう。この例では、有毒なキノコを炒めて食べるよう、ユーザーに伝えてしまうのを防ぐことができました。これによって、ビジネスにおける潜在的な危機がどのようにして回避されるのかお分かりいただけると思います。
さて、上の例は、渡されるパラメーターが、プロシージャー インターフェースが指定するより長かったシナリオです。今度は、渡されるパラメーターが、プロシージャー インターフェースが指定するより短い2つのシナリオを見てみましょう。
次のように、mushroom_name変数の定義および初期化のみを変更した場合はどうなるでしょうか。
Dcl-s mushroom_name Char(19) Inz('Chicken of the Wood');
結果は、少し予想外でした。コンパイラーは、前述のRNF3249エラーではなく、今度はRNF7535エラー「The type and attributes of parameter 1 do not match those of the prototype. (30)(パラメーター1のタイプおよび属性が、プロトタイプのタイプおよび属性に一致しません。(30))」を返します。予想していたのはRNF3249でしたが、結果としてはやはり同じで、コンパイルは失敗でした。期待した通りでした。
では、次のように、プロシージャー インターフェースのパラメーターでConstキーワードを指定したとしたらどうなるでしょうか。
Dcl-s mushroom_name Char(19) Inz('Chicken of the Wood');
Dcl-Pi is_edible_mushroom Ind;
p_mushroom_name Char(20) Const;
End-Pi;
これはまずいことになります。コンパイルが成功するからです。
今度は、渡す側のパラメーターを同じままにして、ConstおよびOptions(*Exact)キーワードの両方が含まれるようにプロシージャー インターフェースを変更してみましょう。
Dcl-s mushroom_name Char(19) Inz('Chicken of the Wood');
Dcl-Pi is_edible_mushroom Ind;
p_mushroom_name Char(20) Const Options(*Exact);
End-Pi;
再コンパイルしてみたところ、やはりコンパイルは成功しています。さて、どういうことでしょうか。しかし、ここではOptions(*Exact)を指定しています。そうであれば、パラメーターは、プロシージャー インターフェースが指定する通りに定義される必要があるのではないでしょうか。結論としては、ConstキーワードとOptions(*Exact)キーワードは、そう考えるようには両立しないということです。どうやら、これらのキーワードを両方一緒に使用しているときには、*Exactがコンパイル失敗を引き起こすのは、プロシージャーに渡される変数が、予期されているより長い場合のみのようです。
まとめると、Options(*Exact)を利用することは、すべてのプログラマーにとって、より防御的なコーディング スタイルを取り入れる一助となるという意味で有用だと思います。ただし、これら2つのキーワードを組み合わせるときは、注意が必要だということです。