メニューボタン
IBMi海外記事2008.11.13

APIリスト処理プログラムに関するV5R4のCLサポート

クレイグ・ルトレッジ 著

この記事では、変数タイプ( pointer、based、defined)、サブルーチン、組み込み関数(%OFFSET)などといったV5R4の新しいCLサポートについて、実際にAPI リスト処理プログラム中での使用例を用いて紹介します。

この記事中に出てくる特記事項は、特に注意してお読みください。"RPG"というタイトルの特記事項は、RPGのデータ構造やポインターになじみのある方々には有益です。CLの機能をRPGの用語で説明し、ご想像になっているより少しだけ異なる部分を取り上げています。"詳細"という事項には興味深い内容が記載されていますが、その機能を必ず使用しなければならないというものではありません。最後に、"注意!"というタイトルの事項には、皆さんが想像する通りには動作しない点が含まれています。

Defined On Variable

現在CLプログラム中でDefined On Variable (DEFVAR)を使用することができると思いますが、本稿を読んでいる間はこれを使用しないでください。変数変更(CHGVAR)と%SST部分文字列組み込み関数(BIF)を一緒に使って変数の一部を取り出す機会は、今までどれくらいあったでしょうか。これからはデータ構造中(CL中のデータ構造のことです)の構文解析はすべて、Declare Variable (DCL)文でDEFVARキーワードを使用することで、可能になります。

従来の%SST部分文字列BIFを使用して、1つの変数の選択された位置にある値と同じ値に別の変数の値をセットする、簡単なCLのプログラム例を、図1に示します。最初のCHGVARコマンドは、&FileLibの1~10の位置にある値と同じ値に&Fileの値をセットします。次のCHGVARコマンドは、&FileLibの11~20の位置にある値に&Libの値をセットします。上記2つのCHGVAR文を実行した結果、&Fileは'A'に、&Libは'B'に、等しくなります。

defined storageとdefined on variableキーワードをDCL文中で使用して同じ結果が得られている様子を、図2に示します。&File用のDCL文をブレークダウンして、この変数が実際どのようにデータ構造のサブフィールドとなったのかを見てみましょう。DCL文中にある、storageキーワードのSTG(*DEFINED)とdefined on variableキーワードのDEFVAR(&FileLib 1)に、着目してください。この2つのキーワードは常に一緒に使用します。STGキーワードは、DEFVARキーワードで指定された変数をオーバーレイするということを意味しています。DEFVARキーワードは、&FileLibがオーバーレイされるキーワードであり、Position 1からオーバーレイされるということを意味しています。

&Lib用のDCL文は、同じSTG(*DEFINED)キーワードとDEFVAR(&FileLib 11)キーワードを使用していますが、11という数字に注意してください。これは&FileLibのオーバーレイの位置が11から始まることを意味しています。

これで、CHGVAR %SST部分文字列文をコードする、代わりの方法がおわかりいただけたでしょうか。DEFVARは「defined on variable」の略語であり、defined variableの話をしているときは「on」という単語が強調されていると覚えておくことが重要です。IBM用語では、&Fileが「defined on」されているということは、&Fileが&FileLibを「オーバーレイ」するのと同義です。したがって、DCL文中にDEFVARキーワードがあったときは、OVERLAYではないかと考えてみてください。

storage (*DEFINED)およびDEFVAR()と一緒に定義された変数がデータ構造サブフィールドと全く同じ動作をするという点に、注意してください。図1では、&Fileを変更した場合、&File中の値だけを変更することになりますが、図2では、&Fileを変更すると、&Fileと&FileLibのポジション1~10の両方を変更することになります。

詳細
DEFVARはdefined on variableへの暗示的なポインターをベースとした変数です。暗示的なポインターはゼロでオフセットされています。暗示的なポインターはデータ構造サブフィールドであると考えると、理解しやすいかもしれません。

サブルーチンのサポート

サブルーチンのサポートは、CLに対する今までの機能強化の中で最高の機能強化です。CLサブルーチンにユニークなのは、整数値をサブルーチン実行文からのパラメータとして、オプションで渡したり受け取ったりすることができる点です。戻り値を使用する方法の1つとして、サブルーチンの進捗状況表示として使用するという方法があります。エラーが発生しなければ0、エラーが見つかれば1を返すことができます。

サブルーチンの簡単な構造を図3に示します。Call Subroutine (CALLSUBR)文では、コントロールがサブルーチンMy1stSubに渡され、My1stSubが2番目のMy2ndSubというサブルーチンを実行しています。ここで順番にCLの戻り値機能が実行されていきます。サブルーチンの実行が終了すると、コントロールはCALLSUBRコマンドの次の行に戻ってきます。

このサブルーチンは、CLPメンバー・タイプまたはCLLEメンバー・タイプのいずれかで動作します。図3に示した通り、あるサブルーチンの中から別のサブルーチンを実行することができます。サブルーチンは、ソースコードの終わりの、メイン・プログラムの最後の実行可能な行の直後とENDPGM文の間に配置しなければなりません。

注意!
サブルーチン実行コマンドの名前は、混乱を招きやすいかもしれません。CALLSUBRとCLサブルーチンは、Call Service Program Procedure API と同様に整数のパラメータを返すことができます。これにより、CLサブルーチンはILE関数と同じように動作すると考えてしまうかもしれません。CLのサブルーチンは、ILEのプロシージャでも関数でもありません。サブルーチンでローカル変数を定義することはできません。サブルーチン中の変数はすべて、メイン・プログラムとプログラム中のその他のあらゆるサブルーチンに対して、グローバル変数として扱われます。CLのサブルーチン名は10文字までに制限されています。

詳細
サブルーチンを再帰呼び出しさせても良いと考えてしまう人がいてもしかたないでしょう。しかし、全ての変数がグローバル変数であるため、再帰呼び出しをしても大丈夫と考える理由を私は思いつきません。もっと奇妙なのは、新しいDeclare Process Options (DCLPRCOPT)コマンドとSubroutine Stack (SUBRSTACK)パラメータの組み合わせです。ここでは、プログラムがCPF0822-サブルーチン・スタック・オーバーフローで異常終了する前にサブルーチンの再帰呼び出しを何回させることができるかを、この組み合わせでシステムに指定します。

不満が多いoutfile処理は脇において

ポインターまたはポインターをベースとした変数を使用することの恩恵をまだ経験していない人の中には、「なぜ使わなければならないのか」と疑問に思われる方がいるのも仕方がないでしょう。

ポインターまたはポインターをベースとした変数を使用することをぜひ検討していただきたい理由は、CLポインターがリスト処理用API のすばらしい世界への扉を開いてくれるからです。リスト処理用API のうちの数百のAPI を使用することで、プログラムに有益な情報を提供してくれます。私が使用しているAPI の中でもっとも良く知られているのは、List Database Relations (QDBLDBR)、List Spooled Files (QUSLSPL)、List Object Information (QUSLOBJ)、List File Members (QUSLMBR)などです。

では、リスト処理用API の使用を検討してみるべきだという理由は何でしょうか。それは余計な苦労を避けるためです。CLプログラムでAPI が使用できるようになる以前は、ファイル・メンバー、オブジェクト、スプール・ファイル、などといったシステム情報のリストを処理する必要がある場合、outfile処理をするしかないというのが一般的でした。しかもその多くの場合、*PRINTを使用するしかありませんでした。

outfile処理は正直なところ、いろいろな観点から見ても使いづらいものです。しかもさらに悪いことに、将来の頭痛の種となりうるのです。たとえば、CLがコンパイルできるようにoutfileを作成しなければなりませんが、IBM側の理由でoutfileが変更になり、そのプログラムが運用環境で異常終了する原因となるかもしれません。*PRINTスプール・ファイルを変更した場合、%SST変数を間違った場所に配置してしまう原因を作ってしまうかもしれません。データを読み込む場合にはデータ・ファイルに対する正しいスプール・ファイルをコピーしているかどうかを確認しなければなりませんし、OSのアップグレードが出力を変更した場合はoutfileプログラムを探して修正し、テストしなおして再実装しなければなりません。パフォーマンスの観点から見ると、CLプログラム中でディスク・ファイルを読むという方法も、最速の選択肢ではありません。

API を使用すると、今まで得られていたのと同じ情報が得られるにもかかわらず(一般的にはより多くの情報が得られます)、outfileに関するあらゆる苦労から逃れることができます。新しいCLの機能は本当すばらしいものなので、V5R4以前でCLプログラミングにリスト処理用APIを使用しようとした場合、本当に大きな恩恵を受けることになります。

ポインター変数とポインター・ベース変数

あなたの関心を惹きたいがためか、誰かがポインター変数やポインター・ベース変数について話し始めると、「データ構造サブフィールドの難しいところはどこだろう」と疑問に思うかもしれません。ポインター・ベース変数は、他の変数の一部をオーバーレイするだけです。ポインター・ベース変数がデータ構造サブフィールドとして動作するという概念を、しっかり頭に入れてください。ポインター変数やポインター・ベース変数を使用しても図2のdefined on variableの例と同じ結果が得られることを、図4に示します。

&PTR1のタイプは*PTRであり、そのポインターが&FileLibのアドレスに設定されている点に注意してください。&PTR2のアドレスは、&FileLibの開始アドレスから10オフセット加えられたところに、設定されています。これを実行すると、&Fileのアドレスは'A'と同じになります。これは、ポインター&PTR1が&FileLibのアドレスに設定されるからです。言い替えると、&Fileは&FileLibのポジション1から始まるデータ構造サブフィールドとして動作するということです。変数&Libは、'B'と等しくなります。これは、ポインター&PTR2が&FileLibのポジション1+10のアドレスに設定されているからです。

"オフセット"とは、ポインターを設定する変数の開始アドレスからのバイト数です。ADDRESSキーワードでオフセットを10に設定すると、開始位置から10個分だけポインターを移動させることを意味しますので、&PTR2はポジション11を指していることになります。

またここで初めて、ソースコード中のDCL文の順序が重要になります。(*PTR)タイプの変数の宣言と初期化は、そのポインターをベースとした変数の宣言より前に配置しなければなりません。

RPG RPGのエントリー・パラメータのアドレスにポインターを設定することができます。しかしCLでは、コンパイル時に「Variable not valid on the ADDRESS keyword.」というエラーになります。ところで、本稿はRPGのポインター・ベース・データ構造に関する知識を前提としてはいません。RPGポインターになじんでいてCL版のポインターを使用したいとお考えであれば、これ以降のRPGのセクションは特に注意してお読みください。RPGとCLのポインターやデータ構造には、大なり小なりの違いがあります。

注意!
CLプログラムをコンパイルして「Error in compiler-created code」というエラーになった場合は、DCL文中で同じ名前の*PTR変数を誤って2箇所で定義しています。コンパイル・リスティングは得られません。

%OFFSET組み込み関数

ポインター・ベース・フィールドが便利なのは、ポインターを別の場所に移動することができるからです。based on変数が任意のフィールド(つまりユーザー空間)をオーバーレイしている場所を変更できるのです。ポインターを移動することで、データ構造サブフィールドがオーバーレイしている場所を制御できるのです。

ポインターの移動(一般的には"ポインター算術"と呼ばれています)は簡単です。自然数(整数)を使用して、ポインターの現在位置から移動したい分だけ加えるか、または減じます。

RPG CLマニュアルにある奇妙な言い回しに気をつけてください。私も長年にわたってポインターを使用してきましたが、「ポインター変数のオフセット部分を変更する」という表現は聞いたことがありません。ポインターを変更する処理について、私はいつも、「ポインターを変更する」という単純な言い方をしてきました。CLポインターはRPGのポインターと全く同様に動作します。使っている用語が違うだけなのです。

詳細
上記の理由は、オペレーティング・システムのシングル・レベル記憶域パラダイムが動作する仕組みと関係があります。システムは、管理下の全ての記憶域(ディスクやメモリー)を「空間」に分割します。データを指し示すポインターは2つの部分からなっています。ポインターが指している空間を示す部分と、その空間内でのオフセットを示す部分です。CLのマニュアルがオフセットのことをポインターの「部分」といっているのは、こうした理由によるものです。また、オペレーティング・システムの安全/セキュリティ機構にも起因しています。オフセットを変更することで、ポインターが異なるメモリー空間を指し示すようにすることはできません。このため、実行可能コードとデータは同じ空間内に格納されることは決してないので、「バッファ・オーバーフロー」が発生することは事実上ありえません。

CLでポインターを移動させるには、%OFFSET (または%OFS) BIFを使用します。

CHGVAR %OFS(&PTR) (%OFS(&PTR) + 11)

この文を実行すると、ポインターとBased On変数が、11バイト分、右へ移動します。

RPG
RPGでは、AAとBBがポインター・フィールドである場合、AAに今までどのような値が入っていたかに全く関係なく、AAのポインター値をAA = BB + 11のように設定することができます。CLは少なくともユーザー空間ではこのようには動作しません。CLでは、%OFFSETを使用する前に、&AAをユーザー空間のアドレスに設定しなければなりません。

API固有の定義

ユーザー空間オブジェクト、API 、リスト処理などの詳細な分析について述べることは本稿の意図ではありませんが、API に直接関連した概念をいくつか定義しておく必要があります。

"ユーザー空間"とは、リスト処理API がリスト・エントリーを読み込むためのオブジェクトです。ユーザー空間についてあまりご存知でない方は、これは非常に長いレコード長をもった、フラットな1レコードのファイルであると考えてみてください。あるいは、2,000バイトの制限を受けないデータ・エリアと考えた方が、よりわかりやすいかもしれません。

"ヘッダー・セクション"とは、ユーザー空間の一部分(ユーザー空間の開始時点からポインター分だけオフセットした場所)で、API がリスト・エントリーの数とその長さを示す場所です。

"リスト・エントリー・セクション"には個々のリスト・エントリー情報が含まれています。これは、リスト・エントリーがユーザー空間内に配列要素のように置かれていると考えると、わかりやすいかもしれません。各リスト・エントリーはその直前のエントリーと隣接しています。IBMがAPI を機能強化したり、バグを修正したりしているので、各エントリーのサイズが変わる可能性があり、その結果、各リスト・エントリーの開始位置や終了位置も変わる可能性があります。そのために、ポインターを使用するのです。ポインターを使用すれば、既存のコードを修正せずにAPI を修正することができます(ポインターを使用することでオーバーレイしている変数の位置を動的に変更することができます)。

"APIのエラー・データ構造"は、APIの呼び出しが正常に終了したのか、あるいは何故正常に終了しなかったのかを示す値を返すためのものです。

プログラム例("SystemiNetwork.com"からダウンロード可能)全体を1行ずつ見ていくことはしませんが、そのかわりに、皆さんが利用できるプログラムの使い方とその動作原理を説明します。

考え方としては、ポインターの値を変更したときは、そのポインターをベースとした変数の内容を変更したことになり、そのポインター・ベース変数をオーバーレイしているすべてのdefined on variableの内容を変更したことになります。

汎用的なヘッダー・データ構造

リスト処理用API はいずれも、"汎用ヘッダー"・データ構造と呼ばれるデータをユーザー空間に返します。この汎用ヘッダー・セクションには、ユーザー空間中のリスト・エントリーを処理するために使用する値が含まれています。ユーザー空間の開始地点のポインター上に変数のベースを置くことで、この情報を取り出すことができます。

図5において、&ApiHeaderは、&HeaderPtrという名前のポインターにベースを置く*BASEDフィールドである点に注意してください。また、*BASEDフィールドをオーバーレイしているサブフィールドにも注意してください。"&ListOffset"はリスト・エントリーの開始位置に対するユーザー空間の開始位置からのバイト数、"&EntryCount"はList Objects (QUSLOBJ) API が返すエントリーの数、"&EntrySize"は各エントリーがユーザー空間内に占めるバイト数です。この値は、次のリスト・エントリーに到達するまでどれくらいポインターを移動させなければならないかを指定しているので、重要な値です。

3つのサブフィールド変数は、&ApiHeaderをオーバーレイする変数として*DEFINEDされています。&ApiHeaderは*BASEDとして定義されており、ユーザー空間の開始位置を示す&HeaderPtrというポインターをベースとしています。

さて、これで開始位置(リスト・オフセット)がわかり、処理しなければならないエントリー数(エントリー・カウント)と各エントリーの長さ(エントリー・サイズ)がわかりました。この3つの情報はいずれも、リスト・エントリーを一通り処理するのに必要な情報です。図6に示した通り、Retrieve Pointer to User Space (QUSPTRUS) API を呼び出して、ポインター変数&HeaderPtrの値をユーザー空間の開始位置に設定します。ここではたくさんのことが起こっています。ユーザー空間の開始位置にポインターを設定すると、図5に示すデータ構造がユーザー空間をオーバーレイし、その結果、サブフィールドがユーザー空間中の汎用ヘッダー・セクションをオーバーレイします。これでサブフィールドには、QUSLOBJ API を呼び出したことで読み込まれたデータが入ることになります。すごいと思いませんか?

リスト・エントリー・データ構造

ユーザー空間中の単一のリスト・エントリーをオーバーレイするQUSLOBJ API のリスト・エントリー・データ構造を、図7に示します。このデータ構造はポインターをベースとしています。ポインターを使用することは以下の考えに基づいています。

  • ポインターを設定し、ユーザー空間中の最初のリスト・エントリーをデータ構造がオーバーレイするようにする
  • リスト・エントリー・データ構造のデータを処理する
  • 次のリスト・エントリーの開始位置にポインターを設定する
  • すべてのリスト・エントリーを処理するまで上記を繰り返す

ポインターが移動するたびに、サブフィールドには異なるリスト・エントリーの情報が入ってきます。API が異なれば、それに対応するリスト・エントリー用に使用するオーバーレイ・データ構造も異なります。List File Members (QUSLMBR) API を使用するCLプログラムを記述するのであれば、図8に示すデータ構造を使用することができます。

データ構造ポインターの移動

(QUSLOBJ APIを呼び出した後の)ユーザー空間の内容を、図9に示します。この図をご覧になると、いくつかの情報を結びつけることができます。ポジション353(AAAを丸で囲んである箇所)をご覧ください、このポジションが最初のリスト・エントリーの開始位置です。ポインターをポジション353に設定し、そのポインターをベースとしたデータ構造を用意したら、図7に示すリスト・エントリー・データ構造を使用してユーザー空間中のこの位置からデータを取り出すことができます。オブジェクト名AAAがライブラリMYLIB中の*FILEタイプPFとなっていることがおわかりいただけます(図8)。

図 5にある汎用ヘッダー・データ構造のエントリー・サイズ・フィールドを見てみると、最初のエントリーは648バイトであることがわかります。つまり、2番目のリスト・エントリーはポジション353(最初のリスト・エントリーへのオフセット)+648(最初のエントリーの長さ)=1001の位置で開始しなければならないということになり(図8)、これは2番目のリスト・エントリーの開始位置にBBBとあるのでおわかりいただけるでしょう。これでポインターが1001の位置に設定されましたので、リスト・エントリー・データ構造には、BBBというオブジェクト名がMYLIB中にタイプ*FILE PFで示されています。

汎用ヘッダーにあるエントリー・カウント変数を使用して、全てのエントリーを処理するのにポインターを何回移動させなければならないかをコントロールします。リスト・エントリー・ポインターを次のエントリーに移動させるためのCLコーディング・テクニックを、図10に示します。最初のCHGVARでは、%OFS (または%OFFSET)を使用して、リスト・エントリー・データ構造のポインターをユーザー空間中の最初のエントリーに設定しています。この文では「ポインターをユーザー空間の開始位置(&HeaderPtr)に移動し、最初のリスト・エントリーの開始位置までのバイト数を加え(&ListOffset)、新しいポインター値をポインター&QuslobjPtrに設定する」と指定しています。

DOFORコマンドは、ユーザー空間に読み込まれているエントリー数(汎用ヘッダーにある&EntryCount)の分だけ繰り返すループを設定しています。
注記: &Countは、*UINTの符号なし整数フィールドです。

CALLSUBRコマンドは、リスト・エントリー・データ構造で定義した変数を使用して記述するサブルーチンを実行します。このコマンドのすばらしいところは、サブルーチンが実行される際、リスト・エントリー・データ構造の全ての変数が、API から引き渡された現在のリスト・エントリーの情報をもっているという点です。

最後のCHGVARは、ポインター(&QuslobjPtr)の現在位置を取り出して現在のエントリーの長さ(&EntrySize)を加え、ポインターを次のエントリーの開始位置に設定します。

API呼び出し用のエラー・パラメータ・データ構造

事実上すべてのAPI は、図11に示すデータ構造を使用して、API の呼び出しが正常に終了したのか、あるいは呼び出しに際してどのようなエラーが発生したのかに関する情報を提供します。CLの新しいdefined on variable機能により、このデータ構造をCLプログラム中でより簡単に使用できるようになりました。

&ApiErrDs変数をオーバーレイしている整数サブフィールドと文字サブフィールドに着目してください。APIの呼び出し時にエラーが発生した場合は、&aProvidedの値が正の値になり、システム・メッセージIDとメッセージ差し替え値が読み込まれます。エラーが発生しなかった場合は、&aProvidedの値は0になり、その他のサブフィールドは無視してもかまいません。

ここで示したプログラム例では、&aProvidedを128に設定してからCreate User Space API を呼び出しています。こうすることで、ユーザー空間が既に存在する場合のエラー・メッセージをAPI が無視することができます。プログラム中のこれ以外の部分については、&aProvidedを0に設定していますので、API エラーが発生した場合はシステム・エラー・メッセージを受け取ります。

QWCCVTDT APIサブルーチン

QUSLOBJ API など、このプログラム例で使用しているAPI も含めたいくつかのAPI は、内部データ・タイプ*DTS (システム内部タイムスタンプ)の値を返しますが、これを人間が読み取り可能なフォーマットに変換するには、Convert Date and Time Format (QWCCVTDT) API を呼び出さなければなりません。

srApiStampという名前の便利なサブルーチンを、図12に示します。このサブルーチンをご自分のプログラム中で使用して、*DTS日付を*ISOフォーマットに変換することができます。1行目では、&LastStamp(リスト・エントリーから受け取った*DTSフォーマットの日付)をサブルーチンで使用する変数に読み込んでいます。このテクニックを使用することで、Created DateやLast Changed Dateを変換する必要がある場合も、同じサブルーチンを使用することができます。この変換サブルーチンは実行されると値が返され、その値が&LastUsed変数に*ISOフォーマットで読み込まれます。

srApiStampサブルーチン内にあるIFコマンドは、入力された日付が16進数のゼロであるかどうかをチェックします。もし16進数のゼロである場合は、ISO文字日付の&ApiIsoも*blanksに設定されます。
注記:16進数のゼロがQUSLOBJ API により読み込まれるのは、そのフィールドに日付値がない場合です。たとえば、新しいオブジェクトには最後に使用された日付の値は入っていません。QWCCVTDT API に16進数のゼロを渡すと、でたらめな値が返ってきます。

ELSEコマンドでは、QWCCVTDT API を呼び出して*DTS(システム・タイムスタンプ)フォーマットを*ISO日付フォーマットに変換しています。

PTFおよびデバッガの問題

お使いのSystem i上にSI28620、SI28196、SI27953の各PTFがインストールされていることを確認してください。インストールされていない場合は、definedあるいはポインター・ベースのフィールドを使用する前に、上記のPTFをインストールしてください。これらのPTFはCRTCLPGMコマンドおよびCRTCLMODコマンドの背後で実行されるオブジェクトだけを変更しますので、システムが稼動中でも上記のPTFをダウンロードして適用することができます(つまりIPLは不要)。PTFをインストールしたら、前述の新しい変数タイプを使用しているCLコードを再コンパイルして、修正を適用する必要があります。これらの新しい変数タイプを使用しているCLコード以外は、再コンパイルする必要はありません。コンパイラは正しいコードを生成するようにアップデートされているので、再コンパイルしたときのみ、「修正された」コードを再生成します。

詳細
上記のPTFは、エントリー・パラメータ上やポインター・ベースのフィールド上にフィールドを定義する際の、以前からあったいくつかの問題点に対処しています。前述の通り、definedのフィールドは実際のところポインターをベースとしたフィールドであり、そのポインターは他の変数内のある位置を指し示しています。つまり、それはdefined onの変数なのです。defined onの変数のアドレスを変更すると、オーバーレイされたフィールドもメモリー内の新しい位置を指し示すように自動的に変更されます。ただし、この機能が最初にリリースされたときは、フィールドの開始位置のアドレスとは異なるアドレスを持った (つまり、サブフィールドへのポインターが、オーバーレイするより大きなフィールドのポインターのアドレスと異なっている) defined onフィールドのポインターに関するバグがありました。したがって、パラメータへのポインター(アドレスが呼び出し側から渡される)や、ベースとするポインターが変わるようなフィールドへのポインターは、正しく動作していませんでした。

注意!
ソース・デバッガとDMPCLPGMには(皮肉なことに)依然としてバグがある点に注意してください。defined on変数がポインター・ベースの変数をオーバーレイしているときは、デバッグ中にそのdefined on変数の値を確認することはできません。図13に示した通り、&QuslobjDSはポインターをベースとした変数で、&ObjLibは&QuslobjDSをオーバーレイしている変数です。デバッグ中に&ObjLibの値を見ることはできません。&ObjLibは正しい値を持っているので、&ObjLibをプログラム中で使用することはできます。問題があるのはプログラム・コードではなく、デバッガなのです。

V5R4のデバッガは、CLコンパイラー上でのdefined変数やbased変数をサポートしていません。IBMは次のリリースでこのデバッガの問題を修正する予定です。IBMはこの問題について、「解決済み/既知の問題に対する解決策」に関する技術情報(APAR)で公開していますので、この問題を改めて報告する必要はありません。

デバッグ中に上記のdefined変数やbased変数の値を確認するには、defined on変数を*AUTO記憶域変数に移動してください(*AUTO記憶域変数という用語は進化し続けるCL用語のひとつとなることは間違いないでしょう)。記憶域タイプ*BASEDおよび*DEFINEDで宣言された変数については、既に説明しました。これら2つの記憶域タイプとして特に定義されていない変数については、デフォルトで*AUTO記憶域となります。*AUTO記憶域変数は、V5R4以前で定義したCL変数と同じタイプの変数になります。図13に示したCHRVARは、ポインターをベースとした変数が通常の(または*AUTO記憶域の)変数&Aに移され、デバッグ中に&Aの値が確認できるようにしています。

V5R4 CL活動中

リスト処理用API を使用すると好都合な点のひとつに、リスト処理用API のいずれも例として使用することができるということが挙げられます。リスト処理用のAPI はいずれも同様に動作するからです。API の品質保証を標準化し、リスト処理用API に適用してきたという意味で、IBMはすばらしい実績を残したといえるでしょう。

ここで紹介したプログラム例は、本稿で説明したV5R4のCLの新機能を全て含んでいます。このプログラムは、QUSLOBJ API を使用して、オブジェクト名のリストと指定された選択パラメータに基づいた詳細情報を生成します。QUSLOBJ API はDSPOBJDコマンドが返す情報と似たような情報を返します。

皆さんが私と同じ考えをお持ちなら、まずコードをダウンロードしてデバッグにかけ、何が起こるのかを見てみるのではないでしょうか。必ず、変数の&ObjectLib (MYLIB)の値をSystem i 上のライブラリに変更してから、プログラム例をコンパイルして実行するようにしてください。プログラム・コードは全く問題がありません。システム上のいずれのオブジェクトも変更することはありませんので、自由にコードを実行してみてこのテクニックを習得してください。QUSLOBJ API を使用してオブジェクトのサイズを取得しようとしているのであれば、変数&ObjSizeにサイズ乗数&SizeMultをかける必要があります。

プログラムを修正してリスト中のオブジェクトに対して動作するようにする(たとえば、選択されたライブラリ中で2000年以降使用されていない全てのオブジェクトを削除する)には、srListEntサブルーチンを変更するだけで十分です。

ここで紹介したソースコードをリスト処理用API のテンプレートとして使用することもできます。修正する必要があるのは、リスト・エントリー・データ構造(API が必要とするフォーマットに変える)、呼び出されるAPI、各リスト・エントリーからのデータを使用するサブルーチンです(それにより、リスト・エントリー・データ構造中で定義されたフィールドを使用できるようにする)。

以上で説明は終わりです。早速今日からCALLSUBRを使い始めて、CLプログラムを論理的に構造化し、冗長なコードを取り除いてください。

あわせて読みたい記事

PAGE TOP