13.5 引数の受渡しの考慮事項

パラメータ渡しモードのマッピングでは、効率性と簡便性の両方のバランスが取られます。プリミティブ型、列挙およびオブジェクト参照の場合、

モードは単純で、プリミティブと列挙用の型P、およびインタフェース型A用の型A_ptrを渡します。

集約型は複雑です。これは、いつ、どのようにパラメータのメモリーを割り当てるか、または割当て解除するかという問題があるためです。inパラメータのマッピングは単純です。これは、パラメータのストレージが呼出し側によって割り当てられ、読取り専用であるためです。outパラメータとinoutパラメータのマッピングは、ほかと比較して問題が多くあります。可変長型の場合、呼出し先はストレージの一部またはすべてを割り当てる必要があります。Point型などの固定長型の場合

(3つの浮動小数点メンバーを含む構造として表される)呼出し側の割当てが推奨されます(スタック割当てを可能にするため)。

両方の割当て方法に対応するために、分割割当てで生じる可能性のある混同を未然に排除します。また、コピーを実行するときに、マッピングで固定長の集約体T用のT&と、可変長のT用のT*&との間に混同が生じないようにする必要があります。このような手法を採る理由は、構造体が固定長か可変長かによって構造体の使用方法が異なることにあります。ただし、呼出し側が管理対象の型T_varを使用する場合でも、マッピングはT_varで一貫しています。

outパラメータとinoutパラメータのマッピングでは、T_varが渡されるときにパラメータ内の以前の可変長データをすべて割当て解除する必要もあります。これらのパラメータの初期値が操作に送信されない場合でも、Oracle Tuxedoには、outパラメータがあります。これは、パラメータに以前の呼出しの結果が格納されている可能性があるためです。T_out型の提供は、T_var型からの変換時にアクセスできないストレージを解放するのに必要なフックを実装に付与することを目的としています。次に、この動作の例を示します。

// IDL
struct S { string name; float age; };
void f(out S p);
// C++
S_var s;
f(s);
// use s
f(s);            // first result will be freed
S *sp;          // need not initialize before passing to out
f(sp);
// use sp
delete sp;     // cannot assume next call will free old value
f(sp);

outパラメータとinoutパラメータの前の値を暗黙的に割当て解除できるのは、T_var型のみで、他の型では使用できません:

// IDL
void q(out string s);
// C++
char *s;
for (int i = 0; i < 10; i++)
q(s);           // memory leak!

ループ内でq関数を呼び出すたびに、メモリー・リークが発生します。これは、呼出し側がout結果でstring_freeを呼び出していないことが原因です。これを修正するには、次に示す2つの方法があります。

// C++
char *s;
String_var svar;
for (int i = 0 ; i < 10; i++) {
    q(s);
    string_free(s);   // explicit deallocation
    // OR:
    q(svar);         // implicit deallocation
}

outパラメータに通常のchar*を使用する場合、呼出し側は変数を毎回outパラメータとして再利用する前に、明示的にメモリーを割当て解除する必要があります。一方、String_varを使用した場合は、変数をoutパラメータとして使用するたびに、割当てがすべて暗黙的に解除されます。

可変長データは、上書きされる前に明示的に解放する必要があります。たとえば、inout文字列パラメータへの割当ての前に、操作の実装者は最初に古い文字データから削除します。同様に、inoutインタフェース・パラメータは、再割り当てされる前に解放される必要があります。パラメータのストレージを確実に解放する方法としては、次の例に示すように、ストレージをローカルT_var変数に割り当てて自動的に解放が行われるようにすることです。

// IDL
interface A;
void f(inout string s, inout A obj);
// C++
void Aimpl::f(char *&s, A_ptr &obj) {
    String_var s_tmp = s;
    s = /* new data */;
    A_var obj_tmp = obj;
    obj = /* new reference */
}

パラメータがポインタ(T*)またはポインタ(T*&)へのリファレンスとして受渡しされる場合、アプリケーションはNULLポインタを受渡しすることができません。これを実行すると、結果は未定義になります。特に、次のどちらかに該当する場合、呼出し側はNULLポインタを渡すことができません。

  • inおよびinout文字列
  • inおよびinout配列(最初の要素へのポインタ)

ただし、呼出し側は、outパラメータにNULL値を持つポインタへのリファレンスを渡すことはできます。これは、呼出し先が値を確認せずに上書きするためです。呼出し先は、次のいずれかに該当する場合、NULLポインタを渡すことができません。

  • outおよび戻り値の可変長の構造体
  • outおよび戻り値の可変長のユニオン
  • outおよび戻り値の文字列
  • outおよび戻り値のシーケンス
  • outおよび戻り値の可変長または固定長の配列
  • outおよび戻り値のany