XDR ライブラリを使用すると、移植の問題が解決するばかりでなく、C の任意のデータ構造を一貫した、文書化された明確な方法で入出力することができます。したがって、ネットワーク上の複数のマシンでデータを共有する場合以外でもXDR ライブラリを使用する意味があります。
XDR ライブラリには、いくつか例を挙げてみるだけでも、文字列 (NULL で終わるバイト配列)、構造体、共用体、配列に対するフィルタルーチンがあります。ユーザーはさまざまなより基本的なルーチンを使用して、独自のXDR ルーチンを作成し、任意のデータ構造体 (配列の要素、共用体のアーム、他の構造体からポイントされるオブジェクト) を記述できます。構造体自体にも、任意の要素の配列や他の構造体へのポインタを持たせることができます。
2 つのサンプルプログラムをより詳しく見てください。XDR ストリーム作成ルーチンファミリがあります。ファミリの各メンバーはビットストリームの扱いが異なります。この例では、標準入出力ルーチンを使用してデータを操作しているので、xdrstdio_create() を使用します。XDR ストリーム作成ルーチンに渡す引数は、ルーチンの機能によって異なります。サンプルプログラムでは、初期化するXDR 構造体へのポインタ、入出力を行う FILE へのポインタ、処理内容の 3 つを引数として渡しています。処理内容は、writer プログラムではシリアライズするので XDR_ENCODE、reader プログラムではデシリアライズするので XDR_DECODE を指定します。
RPC ユーザーは XDR ストリームを作成する必要はありません。RPC システムでストリームが作成され、ユーザーに引き渡されます。
xdr_int() プリミティブは、ほとんどの XDR ライブラリプリミティブと、すべてのクライアント作成の XDR ルーチンに共通の特徴を持っています。第 1 に、このルーチンはエラーが起こると FALSE(0) を返し、正常終了すると TRUE(1) を返します。第 2 に、各データ型 xxx
ごとに対応する XDR ルーチンがあります。XDR ルーチンの形式を次に示します。
xdr_xxx(xdrs, xp) XDR *xdrs; xxx *xp; { } |
このサンプルプログラムの場合は、xxx
は int ですので、対応する XDR プリミティブは xdr_int() になります。クライアントも同様にして、任意の構造体 xxxを定義し、対応するルーチン xdr_int() を作成して、個々のフィールドごとにデータ型に一致する XDR ルーチンを呼び出します。どのデータ型の場合も、最初のパラメータ xdrs は隠されたハンドルとして、そのままプリミティブに渡します。
XDR ルーチンは両方向の変換を行います。すなわち、データをシリアライズするときもデシリアライズするときも同じルーチンを呼び出します。この機能は、移植可能データのソフトウェアエンジニアリングには不可欠な機能です。どちらの変換操作にも同一ルーチンを呼び出すという概念は、シリアライズしたデータはデシリアライズが可能であることをほぼ保証するものです。ネットワークデータを生成するときも使用するときも同じルーチンが使用できます。これは、ルーチンでは必ず、オブジェクト自体ではなくオブジェクトへのポインタを渡すことにより実現されます。デシリアライズの場合だけは、オブジェクト自体が変更されます。この機能は、簡単なサンプルプログラムではわかりませんが、より複雑なデータ構造体をマシン間で引き渡すときに、この機能の価値が明らかになります。必要な場合は、XDR の処理内容を取り出すこともできます。詳細は、「処理内容」の節を参照してください。
もう少し複雑な例を以下に示します。ある人の総資産と負債のデータをプロセス間で交換するとします。このデータは、次のような独自のデータ型が必要なほど重要であるとします。
struct gnumbers { int g_assets; int g_liabilities; }; |
このデータ型に対応する XDR ルーチンでは、この構造体を次のように記述します。
bool_t /* 正常終了では TRUE、異常終了では FALSE */ xdr_gnumbers(xdrs, gp) XDR *xdrs; struct gnumbers *gp; { if (xdr_int(xdrs, &gp->g_assets) && xdr_int(xdrs, &gp->g_liabilities)) return(TRUE); return(FALSE); } |
引数 xdrs は値を調べたり変更したりすることなく、そのままサブコンポーネントルーチンに渡していることに注意してください。XDR ルーチンを呼び出すたびにその戻り値を調べ、異常終了している場合はすぐに FALSE を返して終了しなければなりません。
この例では、bool_t
型が TRUE (1) と FALSE (0) のどちらかの値だけをとる整数として宣言されていることがわかります。このマニュアルでは次の定義を使用します。
#define bool_t int #define TRUE 1 #define FALSE 0 |
これらの取り決めを使用すると、xdr_gnumbers() は次のように書き換えられます。
xdr_gnumbers(xdrs, gp) XDR *xdrs; struct gnumbers *gp; { return(xdr_int(xdrs, &gp->g_assets) && xdr_int(xdrs, &gp->g_liabilities)); } |