この章では、OMG IDL文からC++へのマッピングについて説明します。
注: | この章の一部の情報は、Object Management Group (OMG)の「Common Object Request Broker: C++ Language Mapping Specification」(1999年6月)から転載しています。OMGの許可を得て使用されています。 |
以下では、OMG IDLからC++へのマッピングの次のトピックについて説明します。
各OMG IDLのデータ型は、C++のデータ型またはクラスにマッピングされます。
表13-1に示すように、OMG IDL文の基本データ型は、CORBAモジュールのC++ typedefにマッピングされます。
注: | 長精度型(long)が64ビットのマシンでも、CORBA::Long の定義は32ビット整数を参照します。 |
表13-2に、オブジェクト、擬似オブジェクト、およびユーザー定義型のマッピングを示します。
文字列およびUDTのマッピングの詳細は、以降のセクションを参照してください。
OMG IDLの文字列は、C++のchar *
にマッピングされます。char *
には、制限付き文字列と無制限文字列の両方がマッピングされます。C++のCORBA文字列は、NULLで終了し、char *型の使用時には常に使用できます。
struct
など、ほかのユーザー定義の型の中に文字列がある場合は、CORBA::String_var
型にマッピングされます。これにより、構造体内の各メンバーは自身のメモリーを管理できます。
文字列の割り当ておよび割当て解除には、CORBAクラスの次のメンバー関数を使用する必要があります。
注: | string_alloc 関数によってlen+1 文字が割り当てられるので、結果として得られた文字列には後続のNULL文字を保持するのに十分なスペースを確保できます。 |
OMG IDLでは、任意の文字セットからワイド文字をエンコードするwchar
データ型を定義します。文字データと同じく、実装では任意のコード・セットを使用してワイド文字をエンコードします。ただし、転送にはほかの形式への変換が必要になる場合もあります。wchar
のサイズは、実装によって異なります。
<wide_char_type> ::= “wchar”
wchar_t wmixed[256];
注: | wchar およびwstring データ型を使用すると、ユーザーが記述するネイティブ言語でコンピュータと対話できます。日本語や中国語など、言語によっては一意の文字が多数ある言語もあります。このような文字セットは、1バイトの中に収まりません。マルチ・バイト文字セットをサポートするために様々なスキームが考案されていますが、まだ実用的なレベルには至っていません。ワイド文字とワイド文字列を使用すると、こういった複雑な文字セットとのやり取りが容易になります。 |
wstring
データ型は、ワイド文字NULLを除きwchar
のシーケンスを表します。wstring
型はstring型と似ていますが、その要素型がchar
ではなくwchar
である点で異なります。wstring
の実際の長さは実行時に設定されます。制限付き形式を使用する場合、長さは制限以下でなければなりません。
<wide_string_type> ::= “wstring” “<” <positive_int_const> “>”
| “wstring
CORBA::WString_var v_upper = CORBA::wstring_dup(wmixed);
wstring
型は、unsigned long、char、string、doubleなどの型とまったく同様に作成します。これらの型は、パラメータとして直接使用したり、typedefで指定したり、構造体、シーケンス、ユニオン、配列などの作成に使用したりできます。
注: | wchar およびwstring データ型を使用すると、ユーザーが記述するネイティブ言語でコンピュータと対話できます。日本語や中国語など、言語によっては一意の文字が多数ある言語もあります。このような文字セットは、1バイトの中に収まりません。マルチ・バイト文字セットをサポートするために様々なスキームが考案されていますが、まだ実用的なレベルには至っていません。ワイド文字とワイド文字列を使用すると、こういった複雑な文字セットとのやり取りが容易になります。 |
OMG IDLの定数は、C++のconst
定義にマッピングされます。次に、OMG IDL定義の例を示します。
// OMG IDL
const string CompanyName = “BEA Systems Incorporated”;
module INVENT
{
const string Name = “Inventory Modules”;
interface Order
{
const long MAX_ORDER_NUM = 10000;
};
};
// C++
const char *const
CompanyName = “BEA Systems Incorporated”;
. . .
class INVENT
{
static const char *const Name;
. . .
class Order : public virtual CORBA::Object
{
static const CORBA::Long MAX_ORDER_NUM;
. . .
};
};
最上位の定数は、生成された.h
インクルード・ファイルで初期化されます。ただし、モジュールとインタフェースの各定数は、生成されたクライアント・スタブ・モジュールで初期化されます。
次に、前の例で定義したMAX_ORDER_NUM
定数への有効なリファレンスの例を示します。
CORBA::Long accnt_id = INVENT::Order::MAX_ORDER_NUM;
OMG IDLのenumは、C++のenumにマッピングされます。次に、OMG IDL定義の例を示します。
// OMG IDL
module INVENT
{
enum Reply {ACCEPT, REFUSE};
}
// C++
class INVENT
{
. . .
enum Reply {ACCEPT, REFUSE};
};
次に、前の例で定義したenumへの有効なリファレンスの例を示します。enumへのリファレンスは次のとおりです。
INVENT::Reply accept_reply;
accept_reply = INVENT::ACCEPT;
OMG IDLの構造体は、C++の構造体にマッピングされます。
構造体に対して生成されたコードは、固定長か可変長かによって異なります。固定長型と可変長型の詳細は、「固定長ユーザー定義型と可変長ユーザー定義型」を参照してください。
可変長構造体には、代入演算子メンバー関数が別にあり、2つの可変長構造体間の代入を処理します。
// OMG IDL
module INVENT
{
// Fixed-length
struct Date
{
long year;
long month;
long day;
};
// Variable-length
struct Address
{
string aptNum;
string streetName;
string city;
string state;
string zipCode;
};
};
// C++
class INVENT
{
struct Date
{
CORBA::Long year;
CORBA::Long month;
CORBA::Long day;
};
struct Address
{
CORBA::String_var aptNum;
CORBA::String_var streetName;
CORBA::String_var city;
CORBA::String_var state;
CORBA::String_var zipCode;
Address &operator=(const Address &_obj);
};
};
構造体のメンバーは、適切なC++データ型にマッピングされます。long、shortなどの基本データ型については、表13-1を参照してください。オブジェクト参照、擬似オブジェクト参照、および文字列については、メンバーは次の適切なvarクラスにマッピングされます。
その他のデータ型については、表13-2を参照してください。
生成される構造体にはコンストラクタがないため、メンバーは初期化されません。固定長構造体の場合、集約初期化を行うと初期化できます。例:
INVENT::Date a_date = { 1995, 10, 12 };
可変長メンバーの場合、自己管理型にマッピングします。この型には、メンバーを初期化するコンストラクタが含まれています。
構造体に対してvarクラスが生成されます。詳細は、「varクラスの使い方」を参照してください。
構造体に対してoutクラスが生成されます。詳細は、「outクラスの使い方」を参照してください。
OMG IDLのユニオンは、C++のクラスにマッピングされます。C++クラスには、次のものが格納されています。
// OMG IDL
union OrderItem switch (long)
{
case 1: itemStruct itemInfo;
case 2: orderStruct orderInfo;
default: ID idInfo;
};
// C++
class OrderItem
{
public:
OrderItem();
OrderItem(const OrderItem &);
~OrderItem();
OrderItem &operator=(const OrderItem&);
void _d (CORBA::Long);
CORBA::Long _d () const;
void itemInfo (const itemStruct &);
const itemStruct & itemInfo () const;
itemStruct & itemInfo ();
void orderInfo (const orderStruct &);
const orderStruct & orderInfo () const;
orderStruct & orderInfo ();
void idInfo (ID);
ID idInfo () const;
. . .
};
デフォルトのユニオン・コンストラクタでは、ユニオンのデフォルト区別子の値が設定されていません。そのため、ユニオンの値を設定するまでは、すべてのユニオン・アクセサ・メンバー関数を呼び出すことができません。区別子は、_d
メンバー関数でマッピングされる属性です。
ユニオンのメンバーごとに、アクセサ・メンバー関数とモディファイア・メンバー関数が生成されます。
前の例から抜粋した次のコードでは、2つのメンバー関数はIDメンバー関数に対して生成されています。
void idInfo (ID);
ID idInfo () const;
この例では、最初の関数(モディファイア)で区別子をデフォルト値に、ユニオンの値を指定のID値に設定しています。2番目の関数(アクセサ)では、ユニオンの値を返しています。
ユニオン・メンバーのデータ型によっては、モディファイア関数が追加生成されます。各データ型に対して生成されるメンバー関数は次のとおりです。
basictype
で基本データ型の2つのメンバー関数を生成しています。void basictype (
TYPE
); //モディファイア
TYPE
basictype () const; //アクセサ
objtype
のオブジェクト型およびTypecode型の場合、メンバー関数は次のように生成されます。void objtype (
TYPE); //モディファイア
TYPE
objtype () const; //アクセサ
enumtype
のenum TYPEの場合、メンバー関数は次のように生成されます。void enumtype (
TYPE
); //モディファイア
TYPE
enumtype () const; //アクセサ
void stringInfo (char *); //モディファイア1
void stringInfo (const char *); //モディファイア2
void stringInfo (const CORBA::String_var &); //モディファイア3
const char * stringInfo () const; //アクセサ
char *
パラメータの所有権を想定しており、ユニオンの値が変更されるかユニオンが破棄されたときに、ユニオンはこの文字列に対してCORBA::string_free
メンバー関数を呼び出します。type
への参照で生成されます。void reftype (
TYPE
&); //モディファイア
const
TYPE
& reftype () const; //アクセサ
TYPE
& reftype (); //アクセサ
type
の所有権を想定していません。かわりに、関数はデータ型をコピーします。void arraytype (
TYPE
); //モディファイア
TYPE
_slice * arraytype () const; //アクセサ
type
の所有権を想定していません。かわりに、関数は配列をコピーします。ユニオンに対してvarクラスが生成されます。詳細は、「varクラスの使い方」を参照してください。
ユニオンに対してoutクラスが生成されます。詳細は、「outクラスの使い方」を参照してください。
アクセサとモディファイアのほかに、型TYPEのOMG IDLユニオンに対してswitch (long)区別子で次のメンバー関数が生成されます。
TYPE
();
TYPE
( const
TYPE
& From);
~
TYPE
();
TYPE
&operator=(const
TYPE
& From);
void _d (CORBA::Long Discrim);
Discrim
引数には、新しい区別値を指定します。引数のデータ型は、ユニオンのswitch文で指定したOMG IDLのデータ型で決まります。OMG IDLおよびC++の各データ型については、表13-1を参照してください。
union U switch(long) {
case 1:
case 2:
short s;
case 3:
int it;
};
short st;
U u;
u.s(1296); // member "s" selected
st = u.s(); // st == 1296
u._d(2); // OK: member "s" still selected
st = u.s(); // st == 1296
u._d(3); // BAD_PARAM: selecting a different member
_d()
モディファイアが呼び出されると、Tuxedo C++の「暗黙的な切替」の制約が緩和されます。この場合、例外はスローされず、ユニオンへの影響はありません。 U u2;
u2._d(1); // no exception, union is unchanged
st = u2.s(); // error! accessing an uninitialized union
u2.it(1296); // OK: member "it" now selected
CORBA::Long _d () const;
OMG IDLのシーケンスは、C++のクラスにマッピングされます。C++クラスには、次のものが格納されています。
要素にアクセスする場合、事前に長さを設定しておく必要があります。
// OMG IDL
module INVENT
{
. . .
typedef sequence<LogItem> LogList;
}
// C++
class LogList
{
public:
// Default constructor
LogList();
// Maximum constructor
LogList(CORBA::ULong _max);
// TYPE * data constructor
LogList
(
CORBA::ULong _max,
CORBA::ULong _length,
LogItem *_value,
CORBA::Boolean _relse = CORBA_FALSE
);
// Copy constructor
LogList(const LogList&);
// Destructor
~LogList();
LogList &operator=(const LogList&);
CORBA::ULong maximum() const;
void length(CORBA::ULong);
CORBA::ULong length() const;
LogItem &operator[](CORBA::ULong _index);
const LogItem &operator[](CORBA::ULong _index) const;
static LogItem *allocbuf(CORBA::ULong _nelems);
static void freebuf(LogItem *);
};
};
operator[]
関数は、シーケンス要素へのアクセスまたはシーケンス要素の変更に使用します。この演算子は、シーケンス要素にリファレンスを返します。OMG IDLのベース・タイプは、C++の適切なデータ型にマッピングされます。
基本データ型については、表13-1を参照してください。オブジェクト参照、TypeCodeリファレンス、および文字列の場合、ベース・タイプは生成された_ForSeq_var
クラスにマッピングされます。_ForSeq_var
クラスは、文字列またはシーケンス内に格納されたオブジェクトを更新する機能を備えています。このクラスには、対応するvarクラスと同じメンバー関数とシグネチャがあります。ただし、_ForSeq_var
クラスは、シーケンス・コンストラクタのreleaseパラメータの設定に従います。ほかのクラスとの相違点は、_ForSeq_var
クラスの場合、ユーザーがRelease
フラグを指定できるので、メモリーの解放を制御できることです。
その他のデータ型については、表13-2を参照してください。
シーケンスに対してvarクラスが生成されます。詳細は、「varクラスの使い方」を参照してください。
シーケンスに対してoutクラスが生成されます。詳細は、「outクラスの使い方」を参照してください。
以下では、ベース・タイプがTYPEで指定のOMG IDLシーケンスがSEQの場合に、生成されるシーケンス・クラスのメンバー関数について説明します。
SEQ ();
SEQ (CORBA::ULong Max);
Max
引数には、シーケンスの最大長を指定します。
SEQ (CORBA::ULong Max, CORBA::ULong Length,
TYPE
* Value,
CORBA::Boolean Release);
CORBA_TRUE
の場合、シーケンスはValue
引数が指すバッファの所有権を想定します。Release
フラグがCORBA_ TRUE
の場合、このバッファはallocbuf
メンバー関数で割り当てる必要があります。これは、シーケンスの破棄時に、このバッファをfreebuf
メンバー関数で解放するためです。
SEQ(const S& From);
~SEQ();
SEQ& operator=(const SEQ& From);
Release
フラグがCORBA_TRUE
の場合、現在のシーケンスにある既存の要素はすべて解放されます。From
引数には、コピー元のシーケンスを指定します。
CORBA::ULong maximum( ) const;
void length(CORBA::ULong Length);
Length
引数には、シーケンスの新しい長さを指定します。シーケンスが無制限で新しい長さが現在の最大値を超える場合、バッファが再割り当てされ、要素が新しいバッファにコピーされます。新しい長さが最大値を超える場合、最大値は新しい長さに設定されます。
CORBA::ULong length() const;
TYPE
& operator[](CORBA::ULong Index);
const
TYPE
& operator[](CORBA::ULong Index) const;
Index
引数には、戻り値となる要素の索引を指定します。この索引は、現在のシーケンスの長さを超えることはできません。長さは、TYPE *コンストラクタまたはlength(CORBA::ULong)
モディファイアで設定済ある必要があります。TYPEがオブジェクト参照、TypeCodeリファレンス、または文字列の場合、戻り値の型はForSeq_var
クラスになります。
static
TYPE
* allocbuf(CORBA::ULong NumElems);
TYPE
*
コンストラクタで使用されるバッファを割り当てます。NumElems引数には、バッファ内の割り当てる要素数を指定します。バッファの割当てができない場合、NULLが返されます。
static void freebuf(
TYPE
* Value);
allocbuf
関数で割り当てられたTYPE
*
シーケンス・バッファを解放します。Value
引数には、allocbuf
関数で割り当てられたTYPE
*
バッファを指定します。0(ゼロ)ポインタは無視されます。
OMG IDLの配列は、C++の配列定義にマッピングされます。次に、OMG IDL定義の例を示します。
// OMG IDL
module INVENT
{
. . .
typedef LogItem LogArray[10];
};
// C++
module INVENT
{
. . .
typedef LogItem LogArray[10];
typedef LogItem LogArray_slice;
static LogArray_slice * LogArray_alloc(void);
static void LogArray_free(LogArray_slice *data);
};
配列のスライスとは、元の配列の最初のサイズを除いたすべてのサイズを持つ配列のことです。配列で生成されたクラスのメンバー関数は、スライスへのポインタを使用して配列へのポインタを返します。スライスごとにtypedefが生成されます。
// OMG IDL
typedef LogItem LogMultiArray[5][10];
// C++
typedef LogItem LogMultiArray[5][10];
typedef LogItem LogMultiArray_slice[10];
配列のサイズが1つの場合、配列スライスは型だけになります。たとえば、サイズが1つのlong
の配列がある場合、配列スライスはCORBA::Long
データ型になります。
OMG IDLの配列の型は、構造体と同じ方法でC++の配列要素型にマッピングされます。詳細は、「メンバーのマッピング」を参照してください。
配列に対してvarクラスが生成されます。詳細は、「varクラスの使い方」を参照してください。
配列に対してoutクラスが生成されます。詳細は、「outクラスの使い方」を参照してください。
各配列には、配列の割り当ておよび割当て解除を行う2つの静的関数があります。指定のOMG IDLの型TYPE
の場合、割当てルーチンと割当て解除ルーチンは次のとおりです。
static
TYPE
_slice *
TYPE
_alloc(void);
static void
TYPE
_free(
TYPE
_slice * Value);
OMG IDLの例外は、C++のクラスにマッピングされます。C++クラスには、次のものが格納されています。
生成されるクラスは可変長構造体と似ています。ただし、このクラスは初期化を簡単にするためのコンストラクタ、およびUserExceptionの型を判別するための静的_narrow
メンバー関数が追加されている点で異なります。
// OMG IDL
module INVENT
{
exception NonExist
{
ID BadId;
};
};
// C++
class INVENT
{
. . .
class NonExist : public CORBA::UserException
{
public:
static NonExist * _narrow(CORBA::Exception_ptr);
NonExist (ID _BadId);
NonExist ();
NonExist (const NonExist &);
~NonExist ();
NonExist & operator=(const NonExist &);
void _raise ();
ID BadId;
};
};
Exceptionクラスの属性(データ・メンバー)はパブリックなので、直接アクセスできます。
例外のメンバーは、構造体と同じ方法でマッピングされます。詳細は、「メンバーのマッピング」を参照してください。
例外メンバーはすべて、C++ではパブリック・データなので、直接アクセスできます。
例外に対してvarクラスが生成されます。詳細は、「varクラスの使い方」を参照してください。
例外に対してoutクラスが生成されます。詳細は、「outクラスの使い方」を参照してください。
指定のOMG IDL例外TYPE
の場合、生成されるメンバー関数は次のとおりです。
static
TYPE
* _narrow(CORBA::Exception_ptr Except);
TYPE
例外にナロー変換できる場合に、TYPE
例外へのポインタを返します。例外がナロー変換できない場合は、0(ゼロ)を返します。TYPE
ポインタは、新しいクラスへのポインタではなく、 元の例外ポインタへの型付きポインタです。これは、Exceptパラメータが有効なかぎりにおいて有効です。
TYPE
( );
TYPE
(member-parameters);
TYPE
(const
TYPE
& From);
~
TYPE
();
TYPE
&operator=(const
TYPE
& From);
void _raise ();
CORBA擬似オブジェクトは、通常のCORBAオブジェクトまたはサーバーレス・オブジェクトとして実装できます。CORBA仕様における、これらの戦略の基本的な違いは次のとおりです。
サーバーレス・オブジェクトへのリファレンスは、アドレス空間などのコンピュータ間のコンテキストで有効とはかぎりません。かわりに、パラメータとして渡されるサーバーレス・オブジェクトへのリファレンスでは、このリファレンスの受信側が使用するオブジェクトの、非依存的で機能的に同じコピーが作成されます。これをサポートするには、データ・レイアウトなど、サーバーレス・オブジェクトのほかの隠れた表現プロパティをORBに認識させます。この章では、ORBにサーバーレス・オブジェクトを認識させる仕様については、実装上の詳細事項のため説明は割愛します。
この章では、かわりにすべての擬似オブジェクト型の標準的なマッピング・アルゴリズムについて説明します。これにより、9種類のCORBA擬似オブジェクト型ごとのマッピングについて、説明が断片的にならずに済み、CORBAの将来のリビジョンで提供される可能性のある擬似オブジェクト型にも対応できます。また、Cマッピングの表現に依存せずに、C互換の表現に依存した実装を実現することができます。
C PIDLとは異なり、このマッピングでは、完全なOMG IDLの拡張形式を使用してサーバーレス・オブジェクト型を記述します。擬似オブジェクト型のインタフェースでは、通常のOMG IDLインタフェースと同じ規則に従います。ただし、次の例外があります。
pseudo
が付きます。 pseudo
接頭辞は、そのインタフェースが通常の方法またはサーバーレスの方法のどちらかで実装されることを示します。つまり、以降のセクションで説明する規則か、またはこの章で説明する通常のマッピング規則のどちらかを適用することになります。
サーバーレス・オブジェクトは、このセクションで概説する相違点を除いて、通常のインタフェースと同じ方法でマッピングされます。
サーバーレス・オブジェクト型を表すクラスは、CORBA::Object
のサブクラスではありません。また、ほかのC++クラスのサブクラスである必要はありません。したがって、Object::create_request
などの操作を必ずしもサポートしません。
サーバーレス・オブジェクト型のT
を表す各クラスの場合、次の関数のオーバーロードの各バージョンがCORBAネームスペースで提供されます。
// C++
void release(T_ptr);
Boolean is_nil(T_ptr p);
サブクラスは実装で提供できます。ただし、マッピングされたC++クラスは、ユーザーが容易にサブクラス化できるという保証はありません。実装では、サブクラスに適用されない内部表現やトランスポート形式を想定することは可能です。
サーバーレス・オブジェクト型を表すクラスのメンバー関数は、通常のメモリー管理規則に従うとはかぎりません。これは、CORBA::NVList
などの一部のサーバーレス・オブジェクトが、本質的にほかのサーバーレス・オブジェクトのいくつかのレベルのコンテナでしかない場合があるためです。格納されたサーバーレス・オブジェクトのアクセサ関数から返された値を、呼出し側が明示的に解放しなければならないということは、本来の目的とは反対の使い方になってしまいます。
T_ptr
は、単にT*
のtypedefである必要はありません。// C++
static T_ptr _duplicate(T_ptr p);
static T_ptr _nil();
_duplicate
の有効な実装では、単純に引数を返すか、または新しいインスタンスへのリファレンスを作成します。個別に実装を行うことで、動作の確実性が高くなることがあります。サーバーレス・オブジェクトのインタフェースと宣言がこれらに依存する場合、すべてCマッピングに直接類似しています。マッピングされたC++クラスは、必要に応じて、Cマッピング用に選択したクラスと互換性のある表現を使用して実装することができます。C PIDLとC++ PIDLの擬似オブジェクト仕様の相違点は次のとおりです。
以降のセクションでは、各擬似インタフェースとそのC++マッピングの概説と一覧を示します。以下で定義が行われない型も含め、詳細は、このマニュアルの関連するセクションを参照してください。
OMG IDLのtypedefは、C++のtypedefにマッピングされます。OMG IDLのデータ型によっては、追加のtypedefおよびメンバー関数が定義される場合もあります。各データ型に対して生成されるコードは次のとおりです。
基本データ型は単純なtypedefにマッピングされます。例:
// OMG IDL
typedef long ID;
// C++
typedef CORBA::Long ID;
文字列のtypedefは単純なtypedefにマッピングされます。例:
// OMG IDL
typedef string IDStr;
// C++
typedef char * IDStr;
オブジェクト、インタフェース、およびTypeCodeは、4つのtypedefにマッピングされます。例:
// OMG IDL
typedef Item Intf;
// C++
typedef Item Intf;
typedef Item_ptr Intf_ptr;
typedef Item_var Intf_var;
typedef Item_ptr & Intf _out;
// OMG IDL
typedef LogList ListRetType;
// C++
typedef LogList ListRetType;
typedef LogList_var ListRetType_var;
typedef LogList_out & ListRetType_out;
配列は、メモリーを割り当ておよび解放するために4つのtypedefと静的メンバー関数にマッピングされます。例:
// OMG IDL
typedef LogArray ArrayRetType;
// C++
typedef LogArray ArrayRetType;
typedef LogArray_var ArrayRetType_var;
typedef LogArray_forany ArrayRetType_forany;
typedef LogArray_slice ArrayRetType_slice;
ArrayRetType_slice * ArrayRetType_alloc();
void ArrayRetType_free(ArrayRetType_slice *);
OMG IDLの操作は、C++のメンバー関数にマッピングされます。
メンバー関数の名前が操作の名前になります。操作は、インタフェース・クラスとスタブ・クラスの両方でメンバー関数として定義します。インタフェース・クラスは仮想クラスです。一方、スタブ・クラスは仮想クラスを継承し、クライアント・アプリケーション・スタブのメンバー関数のコードを格納します。オブジェクト・インタフェースで操作が呼び出されると、対応するスタブのメンバー関数に格納されているコードが実行されます。
// OMG IDL
module INVENT
{
interface Order
{
. . .
ItemList modifyOrder (in ItemList ModifyList);
};
};
// C++
class INVENT
{
. . .
class Order : public virtual CORBA::Object
{
. . .
virtual ItemList * modifyOrder (
const ItemList & ModifyList) = 0;
};
};
class Stub_Order : public Order
{
. . .
ItemList * modifyOrder (
const ItemList & ModifyList);
};
生成されたクライアント・アプリケーション・スタブには、スタブ・クラスに対して生成された次のコードが格納されます。
// ROUTINE NAME: INVENT::Stub_Order::modifyOrder
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for operation
// modifyOrder.
// (Interface : Order)
INVENT::ItemList * INVENT::Stub_Order::modifyOrder (
const INVENT::ItemList & ModifyList)
{
. . .
}
操作の各引数は、表13-1および表13-2で説明した、対応するC++の型にマッピングされます。
操作での引数のパラメータ渡しモードについては、表13-7および表13-8を参照してください。
実装メンバー関数のシグネチャは、OMG IDL操作のマッピングされたシグネチャです。クライアント側と異なり、サーバー側のマッピングでは、関数ヘッダーに適切な例外(throw
)指定を含める必要があります。これにより、いつ無効な例外が発生したかをコンパイラで検出できるようになります。これは、ローカルC++からC++ライブラリへの呼出しを行う場合に必要です。この作業を行わない場合、呼出しでは有効な例外をチェックするラッパーを通過してしまいます。例:
// IDL
interface A
{
exception B {};
void f() raises(B);
};
// C++
class MyA : public virtual POA_A
{
public:
void f() throw(A::B, CORBA::SystemException);
...
};
すべての操作および属性が原因でCORBAシステム例外は発生するため、操作にraises
句がない場合でも、すべての例外指定でCORBA::SystemException
を含める必要があります。
メンバー関数内で、「this」ポインタは、クラスで定義された実装オブジェクトのデータを参照します。データにアクセスするだけでなく、メンバー関数は同じクラスで定義されたほかのメンバー関数を暗黙的に呼び出すこともできます。例:
// IDL
interface A
{
void f();
void g();
};
// C++
class MyA : public virtual POA_A
{
public:
void f() throw(SystemException);
void g() throw(SystemException);
private:
long x_;
};
void
MyA::f() throw(SystemException)
{
this->x_ = 3;
this->g();
}
ただし、この方法でサーバント・メンバー関数が呼び出されるときは、CORBAオブジェクトの操作の実装としてではなく、単純にC++のメンバー関数として呼び出されます。このような場合、POA_Current
オブジェクトで利用可能な情報はすべて、メンバー関数の呼出し自体ではなく、C++メンバー関数の呼出しを実行したCORBAリクエストの呼出しを参照します。
一部の既存のORB実装では、対応するインタフェース・クラスから、それぞれスケルトン・クラスが派生します。たとえば、インタフェースがMod::A
の場合、スケルトン・クラスPOA_Mod::A
がMod::A
から派生します。したがって、これらのシステムでは、標準のC++派生ベースの変換規則によって暗黙的にサーバントをオブジェクト参照で取得できます。
// C++
MyImplOfA my_a; // declare impl of A
A_ptr a = &my_a; // obtain its object reference
// by C++ derived-to-base conversion
上のようなコードは、ORB準拠の実装でサポートしていますが、必須ではありません。そのため、移植性はありません。移植性のある同等のコードでは、実装オブジェクトで_this()
を呼び出し、未登録の場合は暗黙的にそれを登録して、そのオブジェクト参照を取得します。
// C++
MyImplOfA my_a; // declare impl of A
A_ptr a = my_a._this(); // obtain its object reference
POAに登録されたオブジェクトでは、オブジェクト識別子としてオクテット、特にPortableServer::POA::ObjectId
型のシーケンスを使用します。ただし、C++のプログラマはオブジェクト識別子として文字列を使用することが多いため、C++マッピングでは、文字列のObjectId
への変換、またはその逆の変換を行ういくつかの変換関数が用意されています。
// C++
namespace PortableServer
{
char* ObjectId_to_string(const ObjectId&);
ObjectId* string_to_ObjectId(const char*);
}
上記の関数は、パラメータ渡しおよびメモリー管理について、標準のC++マッピングの規則に準拠しています。
ObjectId
を文字列に変換したときに、文字列に正しくない文字(NULLなど)が生成された場合は、最初の2つの関数によってCORBA::BAD_PARAM
例外がスローされます。
OMG IDLのモジュールは、C++のクラスにマッピングされます。モジュールに格納されるオブジェクトは、このC++クラスの中で定義します。インタフェースと型もクラスにマッピングされるため、ネストされたC++クラスが生成されます。
// OMG IDL
module INVENT
{
interface Order
{
. . .
};
};
// C++
class INVENT
{
. . .
class Order : public virtual CORBA::Object
{
. . .
}; // class Order
}; // class INVENT
ネストされたモジュールが複数ある場合は、複数のネストされたクラスが生成されます。モジュール内のオブジェクトはモジュール・クラスになります。インタフェース内のオブジェクトはインタフェース・クラスになります。
OMG IDLでは、モジュール、インタフェース、および型の名前を同じにすることができます。ただし、C++言語用のファイルを生成する場合、名前を同じにすることはできません。この制限が必要になるのは、OMG IDLの名前が同じ名前でネストされたC++クラスに生成される際に、これをC++コンパイラでサポートしていないためです。
注: | 現在のモジュール名と同じ名前のインタフェースまたは型でOMG IDLからC++コードを生成すると、Oracle Tuxedo OMG IDLコンパイラでは、その情報を伝えるメッセージが出力されます。このメッセージを無視して、インタフェースまたは型とモジュール名とを区別するために一意の名前を付けなかった場合、コンパイラによってファイルの生成時にエラーが発生したことが通知されます。 |
OMG IDLのインタフェースは、C++のクラスにマッピングされます。このクラスは、OMG IDLインタフェース内にある操作、属性、定数、およびユーザー定義の型(UDT)を格納します。
インタフェースINTFの場合、生成されるインタフェース・コードには、次の項目が格納されます。
// OMG IDL
module INVENT
{
interface Order
{
void cancelOrder ();
};
};
// C++
class INVENT
{
. . .
class Order;
typedef Order * Order_ptr;
class Order : public virtual CORBA::Object
{
. . .
static Order_ptr _duplicate(Order_ptr obj);
static Order_ptr _narrow(CORBA::Object_ptr obj);
static Order_ptr _nil();
virtual void cancelOrder () = 0;
. . .
};
};
オブジェクト参照の型、静的メンバー関数、UDT、操作、および属性については、以降のセクションを参照してください。
ここでは、インタフェースINTFに対して生成される、_duplicate、_narrow
、および_nil
の各静的メンバー関数について詳細に説明します。
static
INTF
_ptr _duplicate (
INTF
_ptr Obj)
CORBA::release
メンバー関数を呼び出して解放する必要があります。エラーが発生した場合、nil INTFオブジェクトへのリファレンスが返されます。引数Obj
には、複製元のオブジェクト参照を指定します。
static
INTF
_ptr _narrow (CORBA::Object_ptr Obj)
CORBA::Object_ptr
オブジェクト参照に付与される、新しいINTFオブジェクト参照を返します。Object_ptr
オブジェクト参照は、CORBA::ORB::string_to_object
メンバー関数を呼び出して作成されているか、または操作からパラメータとして返されています。
INTF
_ptr
オブジェクト参照は、INTFオブジェクト、またはINTFオブジェクトを継承するオブジェクトと対応していなければなりません。新しいINTFオブジェクト参照は、CORBA::release
メンバー関数を呼び出して解放する必要があります。引数Obj
には、INTFオブジェクト参照にナロー変換されるオブジェクト参照を指定します。Obj
パラメータは、このメンバー関数では変更されません。また、不要になったときは解放してください。Obj
がINTFオブジェクト参照にナロー変換できない場合は、INTF nilオブジェクト参照が返されます。
static
INTF
_ptr _nil ( )
インタフェース・クラス(INTF)は仮想クラスです。CORBA標準では、次の処理は許可されていません。
かわりに、オブジェクト参照の型、INTF
_ ptr
クラス、またはINTF
_var
クラスのいずれかを使用します。
オブジェクト参照を取得するには、_narrow
静的メンバー関数を使用します。これらのクラスで操作を呼び出すには、矢印演算子(->
)を使用します。
INTF
_var
クラスは、INTF
_var
クラスがスコープをはずれるか、または再割り当てされるときに、オブジェクト参照を自動的に解放することでメモリー管理を簡単にします。変数の型は、多くのUDTに対して生成されます。「varクラスの使い方」では、変数の型について説明しています。
OMG IDLの読取り専用の属性は、属性値を返すC++関数にマッピングされます。読み書き属性は、2つのオーバーロードC++関数にマッピングされます。2つの関数で1つは属性値を返し、もう1つは属性値を設定します。オーバーロードのメンバー関数の名前が属性の名前になります。
属性は操作の生成と同じ方法で生成されます。属性は、仮想クラスとスタブ・クラスの両方で定義します。次に、OMG IDL定義の例を示します。
// OMG IDL
module INVENT
{
interface Order
{
. . .
attribute itemStruct itemInfo;
};
};
// C++
class INVENT
{
. . .
class Item : public virtual CORBA::Object
{
. . .
virtual itemStruct * itemInfo ( ) = 0;
virtual void itemInfo (
const itemStruct & itemInfo) = 0;
};
};
class Stub_Item : public Item
{
. . .
itemStruct * itemInfo ();
void itemInfo (
const itemStruct & itemInfo);
};
生成されたクライアント・アプリケーション・スタブには、スタブ・クラスに対して生成された次のコードが格納されます。
// ROUTINE NAME: INVENT::Stub_Item::itemInfo
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
INVENT::itemStruct * INVENT::Stub_Item::itemInfo ( )
{
. . .
}
//
// ROUTINE NAME: INVENT::Stub_Item::itemInfo
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
void INVENT::Stub_Item::itemInfo (
const INVENT::itemStruct & itemInfo)
{
}
属性は、属性値を返す操作と属性を設定する操作の2つの操作と等価です。たとえば、上記のitemInfo
属性は次と等価です。
void itemInfo (in itemStruct itemInfo);
itemStruct itemInfo ();
属性の引数のマッピングは、操作の引数のマッピングと同じです。属性は、対応するC++の型にマッピングされます。C++の型については、表13-1および表13-2を参照してください。操作での引数のパラメータ渡しモードについては、表13-7および表13-8を参照してください。
OMG IDLのany
は、CORBA::Any
クラスにマッピングされます。CORBA::Any
クラスは、型保障の方法でC++の型を処理します。
一致しないTypeCodeや値でany
が作成される可能性を低くするには、C++関数のオーバーロード機能を使用します。具体的には、OMG IDL仕様の特定の型ごとに、その型の値を挿入および抽出するためのオーバーロード関数が用意されています。ネームスペースの純粋さが損なわれるのを完全に防ぐには、これらの関数にオーバーロード演算子を使用します。これらの関数については以下で詳細に説明しますが、特徴としては、any
に挿入またはanyから抽出される値のC++の型によって適切なTypeCodeが示されるという点です。
後述する型保障any
インタフェースはC++関数のオーバーロードに基づいているため、OMG IDL仕様から生成されるC++の型は別々のものである必要があります。ただし、この要件が満たされない特別な場合があります。
char*
にマッピングされます。そのため、制限付き文字列値でany
を作成または設定する方法が別に必要となります。この方法については、「Boolean、Octet、Char、および制限付き文字列の識別」を参照してください。any
を作成または設定する方法については、下記および「配列」を参照してください。 型保障の方法でany
の値を設定できるようにするために、OMG IDLの型Tごとに次のオーバーロード演算子関数が個別に用意されています。
// C++
void operator<<=(Any&, T);
この関数のシグネチャでは、次の型があれば十分です。通常、これらの型は値によって渡されます。
型Tの値が大きすぎて完全に値を渡すことができない場合、次の2つの形式の挿入関数が用意されています。
// C++
void operator<<=(Any&, const T&); // copying form
void operator<<=(Any&, T*); // non-copying form
コピー形式は、呼出し側に関して見れば、最初に示した形式とほぼ同じです。
上記の「左シフト代入」演算子は、次のように型付き値をany
に挿入するために使用します。
// C++
Long value = 42;
Any a;
a <<= value;
この場合、型Long
にオーバーロードされたoperator<<=
のバージョンは、Any変数の値とTypeCodeの両方を適切に設定します。
operator<<=
を使用してany
の値を設定することは、次のことを意味します。
operator<<=
のコピー・バージョンの場合、Anyの値の存続期間は、operator<<=
に渡された値の存続期間に依存しません。Anyの実装では、operator<<=
に渡された値への参照またはポインタとして、その値を格納しません。operator<<=
の非コピー・バージョンの場合、挿入されたT*
はAnyが処理します。挿入後はAnyがT*の所有権を想定するため、呼出し側は指されたデータにT*
を使用してアクセスできず、Anyは指されたデータを直ちにコピーして元のデータを破棄します。operator<<=
のコピーと非コピーの両バージョンでは、Any
が保持していた前の値がすべて適切に割当て解除されます。たとえば、「型付けされていない値の処理」で説明したAny(TypeCode_ptr,void*,TRUE)
コンストラクタを呼び出してAny
を作成した場合、Any
はvoid*
が指すメモリーの割当てを解除してから、新しい値をコピーします。// C++
void operator<<=(Any&, const char*);
文字列型はすべてchar*
にマッピングされるので、この挿入関数は、挿入された値が無制限文字列であることを想定します。制限付き文字列を正しくAnyに挿入する方法については、「Boolean、Octet、Char、および制限付き文字列の識別」
を参照してください。制限付き文字列と無制限文字列の挿入をコピーせずに実行するには、Any::from_string
ヘルパー型を使用します。このヘルパー型については、「Boolean、Octet、Char、および制限付き文字列の識別」を参照してください。
型保障で配列を挿入するには、「配列」で説明しているArray_forany
型を使用します。ORBでは、Array_forany
型ごとに、オーバーロードされたoperator<<=
のバージョンが用意されています。例:
// IDL
typedef long LongArray[4][5];
// C++
typedef Long LongArray[4][5];
typedef Long LongArray_slice[5];
class LongArray_forany { ... };
void operator<<=(Any &, const LongArray_forany &);
Array_forany
型は、const
への参照によって常にoperator<<=
に渡されます。Array_forany
コンストラクタのnocopy
フラグは、挿入された値をコピーするか(nocopy == FALSE
)、処理するか(nocopy == TRUE
)を制御するために使用します。nocopy
フラグはデフォルトでFALSE
に指定されているため、デフォルトでは挿入がコピーされます。
T
の配列とT*
には型にあいまいさがあるため、移植性のあるコードの場合は、配列をAnyに挿入するときに適切なArray_forany
型を明示的に使用することをお薦めします。例:
// IDL
struct S {... };
typedef S SA[5];
// C++
struct S { ... };
typedef S SA[5];
typedef S SA_slice;
class SA_forany { ... };
SA s;
// ...initialize s...
Any a;
a <<= s; // line 1
a <<= SA_forany(s); // line 2
行1の場合、配列型SA
がその最初の要素へのポインタに集束されるため、コピーSA_forany
挿入演算子ではなく、非コピーoperator<<=(Any&, S*)
が呼び出されます。行2の場合、SA_forany
型が明示的に作成されるので、目的の挿入演算子が呼び出されます。
オブジェクト参照のoperator<<=
の非コピー・バージョンでは、次のようにT_ptr
型のアドレスを取得します。
// IDL
interface T { ... };
// C++
void operator<<=(Any&, T_ptr); // copying
void operator<<=(Any&, T_ptr*); // non-copying
非コピーのオブジェクト参照の挿入では、T_ptr*
が指すオブジェクト参照を処理します。したがって、Anyが元のオブジェクト参照を複製して直ちに解放するため、挿入後に呼出し側は、T_ptr
が参照するオブジェクトにアクセスすることができません。呼出し側は、T_ptr
自体のストレージの所有権を保持します。
operator<<=
のコピー・バージョンは、Any_var
型でもサポートされます。
型保障の方法でany
から値を取得できるようにするために、ORBではOMG IDLの型Tごとに次の演算子が用意されています。
// C++
Boolean operator>>=(const Any&, T&);
この関数のシグネチャでは、プリミティブ型があれば十分です。通常、この型は値によって渡されます。型Tの値が大きすぎて完全に値を渡すことができない場合、ORBでは次のように別のシグネチャが提供されます。
// C++
Boolean operator>>=(const Any&, T*&);
その他の型については、すべて関数の2番目の形式が使用されます。
上記の「右シフト代入」演算子は、次のように型付き値をany
から抽出するために使用します。
// C++
Long value;
Any a;
a <<= Long(42);
if (a >>= value) {
// ... use the value ...
}
この場合、型Long
のoperator>>=
のバージョンは、Anyが型Long
の値を格納しているかどうかを判別します。格納している場合、呼出し側によって提供されたリファレンス変数にその値をコピーし、TRUE
を返します。Anyが型Long
の値を格納していない場合、呼出し側のリファレンス変数の値は変更されず、operator>>=
はFALSE
を返します。
型が非プリミティブの場合、ポインタが抽出を行います。次に、OMG IDL構造体の例を示します。
// IDL
struct MyStruct {
long lmem;
short smem;
};
// C++
Any a;
// ... a is somehow given a value of type MyStruct ...
MyStruct *struct_ptr;
if (a >>= struct_ptr) {
// ... use the value ...
}
抽出に成功した場合、呼出し側のポインタはAnyが管理するストレージを指し、operator>>=
はTRUE
を返します。呼出し側は、このストレージのdelete
または解放を試行する必要はありません。また、代入、挿入、またはreplace
関数によってAny変数が置き換えられた後、またはAny変数が破棄された後に、呼出し側はストレージを使用する必要もありません。ただし、これらの抽出演算子でT_var
型を使用しないよう注意する必要があります。これは、Anyによって所有されるストレージを抽出演算子が削除しようとするためです。
抽出に失敗した場合、呼出し側のポインタの値はNULLポインタと同じに設定され、operator>>=
はFALSE
を返します。
配列型を正しく抽出するには、Array_forany
型を使用します。この型については、「配列」を参照してください。
// IDL
typedef long A[20];
typedef A B[30][40][50];
// C++
typedef Long A[20];
typedef Long A_slice;
class A_forany { ... };
typedef A B[30][40][50];
typedef A B_slice[40][50];
class B_forany { ... };
Boolean operator>>=(const Any&, A_forany&); // for type A
Boolean operator>>=(const Any&, B_forany&); // for type B
Array_forany
型は、リファレンスによって常にoperator>>=
に渡されます。
文字列および配列の場合、アプリケーションはAnyのTypeCodeをチェックします。これは、抽出値を使用する際に、アプリケーションが確実に配列オブジェクトまたは文字列オブジェクトのバウンドを超えないようにするためです。
operator>>=
は、Any_var
型でもサポートされます。
OMG IDLの型のBoolean、octet、およびcharは、特定のC++型にマッピングする必要はありません。そのため、型保障Anyインタフェースで使用できるようにするために、これらの型を互いに識別する方法が別に必要となります。同様に、制限付き文字列と無制限文字列は共にchar*
にマッピングされるため、両者を識別する方法が別に必要となります。ここでは、識別を行うために、Anyクラス・インタフェース内でネストされた新しいヘルパー型をいくつか導入します。ヘルパー型を使用した識別の例を次に示します。
// C++
class Any
{
public:
// special helper types needed for boolean, octet,
// char, and bounded string insertion
struct from_boolean {
from_boolean(Boolean b) : val(b) {}
Boolean val;
};
struct from_octet {
from_octet(Octet o) : val(o) {}
Octet val;
};
struct from_char {
from_char(Char c) : val(c) {}
Char val;
};
struct from_string {
from_string(char* s, ULong b,
Boolean nocopy = FALSE) :
val(s), bound(b) {}
char *val;
ULong bound;
};
void operator<<=(from_boolean);
void operator<<=(from_char);
void operator<<=(from_octet);
void operator<<=(from_string);
// special helper types needed for boolean, octet,
// char, and bounded string extraction
struct to_boolean {
to_boolean(Boolean &b) : ref(b) {}
Boolean &ref;
};
struct to_char {
to_char(Char &c) : ref(c) {}
Char &ref;
};
struct to_octet {
to_octet(Octet &o) : ref(o) {}
Octet &ref;
};
struct to_string {
to_string(char *&s, ULong b) : val(s), bound(b) {}
char *&val;
ULong bound;
};
Boolean operator>>=(to_boolean) const;
Boolean operator>>=(to_char) const;
Boolean operator>>=(to_octet) const;
Boolean operator>>=(to_string) const;
// other public Any details omitted
private:
// these functions are private and not implemented
// hiding these causes compile-time errors for
// unsigned char
void operator<<=(unsigned char);
Boolean operator>>=(unsigned char &) const;
};
ORBでは、これらの特別なヘルパー型用に、オーバーロードのoperator<<=
およびoperator>>=
関数を提供します。これらのヘルパー型は、次のように使用します。
// C++
Boolean b = TRUE;
Any any;
any <<= Any::from_boolean(b);
// ...
if (any >>= Any::to_boolean(b)) {
// ...any contained a Boolean...
}
char* p = "bounded";
any <<= Any::from_string(p, 8);
// ...
if (any >>= Any::to_string(p, 8)) {
// ...any contained a string<8>...
}
Anyへの制限付き文字列または無制限文字列の挿入をコピーせずに行う場合、from_string
コンストラクタでnocopy
フラグをTRUE
に設定する必要があります。
// C++
char* p = string_alloc(8);
// ...initialize string p...
any <<= Any::from_string(p, 8, 1); // any consumes p
boolean、charおよびoctetがすべてC++型のunsigned
char
にマッピングされる場合、boolean、charまたはoctet型のanyの直接挿入または抽出を試行すると、unsigned
char
用のプライベートで未実装のoperator<<=
およびoperator>>=
関数によって、コンパイル・エラーが発生します。
// C++
Octet oct = 040;
Any any;
any <<= oct; // this line will not compile
any <<= Any::from_octet(oct); // but this one will
Anyからオブジェクト参照を基本Object型として抽出する方が望ましい場合があります。これを実行するには、boolean、char、およびoctetの抽出に必要なヘルパー型と同様のヘルパー型を使用します。
// C++
class Any
{
public:
...
struct to_object {
to_object(Object_ptr &obj) : ref(obj) {}
Object_ptr &ref;
;
Boolean operator>>=(to_object) const;
...
};
to_object
ヘルパー型は、Anyからオブジェクト参照を基本Object型として抽出するために使用します。AnyにそのTypeCodeとして示されたオブジェクト参照型の値がある場合、抽出関数operator>>=(to_object)
は、その格納されているオブジェクト参照を明示的にObject型に拡大し、TRUE
を返します。それ以外の場合は、FALSEを返します。抽出されたオブジェクト参照の型の拡大を実行するのは、オブジェクト参照抽出関数のみです。通常のオブジェクト参照の抽出と同じく、to_object
抽出演算子は、オブジェクト参照を複製しません。
状況によっては、Anyへの型保障インタフェースでは不十分な場合があります。たとえば、データ型がバイナリ形式でファイルから読み取られ、型Anyの値を作成するために使用される状況などです。このような場合、Anyクラスは、コンストラクタに明示的なTypeCodeとジェネリック・ポインタを提供します。
// C++
Any(TypeCode_ptr tc, void *value, Boolean release = FALSE);
コンストラクタは、指定のTypeCode擬似オブジェクト参照を複製します。release
パラメータがTRUE
の場合、Anyオブジェクトは、value
パラメータが指すストレージの所有権を想定します。 せん。value
パラメータがrelease=TRUE
の指定でAnyに対して処理された後は、呼出し側はこのパラメータの存続期間を想定できません。これは、Anyがvalue
パラメータをコピーして、直ちに元のポインタを解放するためです。release
パラメータがデフォルトのFALSE
の場合、Anyオブジェクトは、value
によって指されるメモリーを呼出し側が管理すると想定します。value
パラメータはNULLポインタでもかまいません。
// C++
void replace(
TypeCode_ptr,
void *value,
Boolean release = FALSE
);
TypeCode_ptr type() const;
const void *value() const;
replace
関数は、型保障挿入インタフェースで使用不可能な型で使用します。したがって、この関数は上述のコンストラクタに似ています。既存のTypeCodeは解放され、必要に応じて値のストレージの割当てが解除されます。TypeCode関数のパラメータは複製されます。release
パラメータがTRUE
の場合、Anyオブジェクトは、value
パラメータが指すストレージの所有権を想定します。value
パラメータがrelease=TRUE
の指定でAny::replace
関数に対して処理された後は、Anyはこのパラメータの存続期間を想定できません。これは、Anyがvalue
パラメータをコピーして、直ちに元のポインタを解放するためです。release
パラメータがデフォルトのFALSE
の場合、Anyオブジェクトは、値が入っているメモリーを呼出し側が管理すると想定します。replace
関数のvalue
パラメータはNULLポインタでもかまいません。
上記のコンストラクタもreplace
関数も型保障ではありません。特に、TypeCodeとvoid*
引数の実際の型との一貫性については、実行時にコンパイラは保証しません。一致しないTypeCodeと値でAny
が作成される場合の、ORB実装の動作は定義されません。
type
関数は、Anyに関連付けられたTypeCodeにTypeCode_ptr
擬似オブジェクト参照を返します。すべてのオブジェクト参照の戻り値と同様に、リファレンスが不要になったときに呼出し側は、それを解放するか、または自動管理を行うためにTypeCode_var
変数を割り当てる必要があります。
value
関数は、Anyに格納されているデータへのポインタを返します。Anyに関連付けられている値がない場合、value
関数はNULLポインタを返します。
デフォルト・コンストラクタは、値を作成せずに、型tk_null
のTypeCodeでAnyを作成します。コピー・コンストラクタは、AnyパラメータのTypeCode_ptr
で_duplicate
を呼び出し、パラメータの値をディープ・コピーします。代入演算子は、それ自身のTypeCode_ptr
を解放し、必要に応じて現在の値のストレージを割当て解除します。その後、AnyパラメータのTypeCode_ptr
を複製し、パラメータの値をディープ・コピーします。デストラクタは、TypeCode_ptr
でrelease
を呼び出し、必要に応じて値のストレージを割当て解除します。
その他のコンストラクタについては、「型付けされていない値の処理」を参照してください。
Anyクラスの定義の詳細は、「Anyクラスのメンバー関数」を参照してください。
このセクションは、Object Management Group (OMG)の「Common Object Request Broker: Architecture and Specification, Revision 2.4.2」(2001年2月)の第3章、第5章、および第6章、および「CORBA C++ Language Mapping Specification」(1999年6月)に記載されている情報に基づいています。OMGの許可を得て使用されています。
特に、オブジェクトがサポートするオブジェクト・インタフェース型は、IDLインタフェースで定義するので、任意の実装が可能になります。分散オブジェクト・システムでは、実装上の制約がほとんどないので、大きな値が入ります。ただし、リファレンスよりも値でオブジェクトを渡すことが可能な方が望ましい場合が多くあります。これは、オブジェクトの主要な「目的」がデータをカプセル化する場合や、アプリケーションでオブジェクトの「コピー」を明示的に作成するのが好ましい場合に、特に便利です。
値でオブジェクトを渡すセマンティクスは、標準のプログラミング言語のセマンティクスとほぼ同じです。値で渡されるパラメータの受信側は、オブジェクトの「状態」の記述を受信します。次に、受信側は新しいインタンスをその状態でインスタンス化します。その際、送信側のインスタンスのIDを個別に付けます。パラメータ渡しの操作が完了すると、2つのインスタンス間には何の関係もないものと見なされます。
受信側はインスタンスをインスタンス化する必要があるため、オブジェクトの状態と実装に関する情報を認識している必要があります。そこで、値型では、CORBA構造体とCORBAインタフェースをブリッジする、次のセマンティクスを備えています。
値型の基本概念は比較的単純です。ある意味で値型は、「通常の」IDLインタフェース型と構造体との中間的なものと言えます。アプリケーションのプログラマは、インタフェース型に関係なく追加のプロパティ(状態)と実装の詳細が指定されることを、値型を使用して通知します。この情報の指定は、インタフェース型に関係なく実装上の制約として追加されます。これは、ここで指定したセマンティクスと言語マッピングの両方に反映されます。
値型(値で渡すことが可能なオブジェクト)のサポート以前は、CORBAオブジェクトには、すべてオブジェクト参照がありました。そのため、複数のクライアントが特定のオブジェクトを呼び出すとき、クライアントは同じリファレンスを使用していました。オブジェクトのインスタンスはサーバーORB上に残り、その状態はクライアントORBではなく、サーバーORBが管理していました。
値型は、CORBAアーキテクチャへの重要な追加機能であると言えます。参照によって受け渡されるオブジェクトに関して、値型には状態とメソッドが含まれますがオブジェクト参照は含まれず、プログラミング言語オブジェクトとして常にローカルで呼び出されます。受信側からのリクエスト時に、値型は送信側のコンテキストの状態をパッケージ化し、「over the wire」状態を受信側に送信します。受信側では、インスタンスが作成され、送信された状態が自動的に設定されます。この段階以降、送信側はクライアント側のインスタンスを制御できなくなります。つまり、これ以降は、受信側がインスタンスをローカルで呼び出すことができるようになります。このモデルにより、ネットワーク経由の通信で生じる遅延を短縮することができます。この遅延は、大規模なネットワークで顕著に生じます。値型を追加することにより、CORBA実装の規模の調整が容易になり、大量のデータ処理要件を満たすことができます。
このように、値型の本質的な特徴は、その実装が常にローカルである点です。つまり、具象的なプログラミング言語で値型を明示的に使用すると、常にローカルで実装が行われ、リモート呼出しが不要になることが保証されます。値型の値自体がIDであるため、値型にはIDがなく、ORBに「登録」されません。
次に、IDLの値型の例を示します。これは、Object Management Group (OMG)の「CORBA C++ Language Mapping Specification」(1999年6月)から引用したものです。
// IDL
valuetype Example {
short op1();
long op2(in Example x);
private short val1;
public long val2;
private string val3;
private float val4;
private Example val5;
};
The C++ mapping for this valuetype is:
// C++
class Example : public virtual ValueBase {
public:
virtual Short op1() = 0;
virtual Long op2(Example*) = 0;
virtual Long val2() const = 0;
virtual void val2(Long) = 0;
static Example* _downcast(ValueBase*);
protected:
Example();
virtual ~Example();
virtual Short val1() const = 0;
virtual void val1(Short) = 0;
virtual const char* val3() const = 0;
virtual void val3(char*) = 0;
virtual void val3(const char*) = 0;
virtual void val3(const String_var&) = 0;
virtual Float val4() const = 0;
virtual void val4(Float) = 0;
virtual Example* val5() const = 0;
virtual void val5(Example*) = 0;
private:
// private and unimplemented
void operator=(const Example&);
};
class OBV_Example : public virtual Example {
public:
virtual Long val2() const;
virtual void val2(Long);
protected:
OBV_Example();
OBV_Example(Short init_val1, Long init_val2,
const char* init_val3, Float init_val4,
Example* init_val5);
virtual ~OBV_Example();
virtual Short val1() const;
virtual void val1(Short);
virtual const char* val3() const;
virtual void val3(char*);
virtual void val3(const char*);
virtual void val3(const String_var&);
virtual Float val4() const;
virtual void val4(Float);
virtual Example* val5() const;
virtual void val5(Example*);
// ...
};
ユーザー定義型のメモリー管理規則およびメンバー関数のシグネチャは、その型が固定長か可変長かどうかによって異なります。次のいずれか1つに該当する場合、ユーザー定義型は可変長です。
自動変数varは、メモリー管理を簡単に行うために用意されています。varはvarクラスを通じて提供されます。このクラスは、型に必要なメモリーの所有権を想定し、varオブジェクトのインスタンスが破棄されたり、新しい値がvarオブジェクトに割り当てられたときにメモリーを解放します。
Oracle Tuxedoでは、次の型のvarクラスが用意されています。
varクラスのメンバー関数は共通ですが、OMG IDLの型によって演算子を追加でサポートする場合があります。OMG IDLの型TYPEの場合、TYPE_varクラスは、コンストラクタ、デストラクタ、代入演算子、および基底のTYPE型にアクセスするための演算子を格納しています。次に、varクラスの例を示します。
class TYPE_var
{
public:
// constructors
TYPE_var();
TYPE_var(TYPE *);
TYPE_var(const TYPE_var &);
// destructor
~TYPE_var();
// assignment operators
TYPE_var &operator=(TYPE *);
TYPE_var &operator=(const TYPE_var &);
// accessor operators
TYPE *operator->();
TYPE *operator->() const;
TYPE_var_ptr in() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
operator TYPE_ptr;
operator const TYPE_ptr&() const;
operator TYPE_ptr&();
};
TYPE_var()
TYPE_var
クラスのデフォルトのコンストラクタです。このコンストラクタは0(ゼロ)に初期化します。この値は、varクラスがTYPE *
を所有することを示します。有効なTYPE *
を割り当てていない限り、TYPE_var
クラスでoperator->を呼び出すことはできません。
TYPE_var(TYPE * Value);
Value
引数は、このvarクラスが所有するTYPEへのポインタです。このポインタは0(ゼロ)に指定しないでください。
TYPE_var(const TYPE_var & From);
From
パラメータが所有するTYPEに格納されているデータをディープ・コピーします。TYPE_varが破棄されると、TYPEのコピーは解放または削除されます。From
パラメータには、コピー元のTYPEを指すvarクラスを指定します。
~TYPE_var();
CORBA::string_free
ルーチンです。その他の参照の場合は、 CORBA::release
ルーチンです。その他の型の場合はdelete
、または割当て済メモリーを解放するために生成された静的ルーチンです。
TYPE_var &operator=(TYPE * NewValue);
NewValue
パラメータが指すTYPEの所有権を想定します。現在、TYPE_varがTYPEを所有している場合、それを解放してから、NewValue
パラメータの所有権を想定します。NewValue
引数は、このvarクラスが所有するTYPEへのポインタです。このポインタは0(ゼロ)に指定しないでください。
TYPE_var &operator=(const TYPE_var &From);
From
TYPE_varパラメータが所有するTYPEに格納されているデータをディープ・コピーします。現在、TYPE_varがTYPEを所有している場合は解放されます。TYPE_varが破棄されると、TYPEのコピーは解放されます。From
パラメータには、コピー元のデータを指すvarクラスを指定します。
TYPE *operator->();
TYPE *operator->() const;
operator->
を使用できません。TYPE_varが破棄された後に、この戻り値の解放またはアクセスを試行しないでください。
TYPE_var_ptr in() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
in
パラメータを渡すには、in()
メンバー関数を、inout
パラメータを渡す場合はinout()
メンバー関数を、out
パラメータを渡す場合はout()
メンバー関数をそれぞれ呼び出します。TYPE_varから戻り値を取得するには、_return()
関数を呼び出します。各TYPE
_var型について、これらの関数の戻り値の型はそれぞれ、表13-7で示すin
、inout
、out
、および基底の型TYPEの戻り値モードの型にそれぞれ一致します。
ユーザー定義のデータ型によって、サポートされる演算子は一部異なります。表13-3では、生成されたC++コードにおける各OMG IDLのデータ型でサポートされる様々な演算子について説明しています。表13-3で示すように、代入演算子はすべてのデータ型でサポートされるので、比較には含まれていません。
表13-4では、シグネチャを示します。
シーケンスvarでは、次のoperator[]メンバー関数を追加でサポートします。
TYPE &operator[](CORBA::ULong Index);
operator[]
を呼び出します。operator[]
は、指定の索引でシーケンスの適切な要素へのリファレンスを返します。Index
引数には、戻り値となる要素の索引を指定します。この索引は、現在のシーケンスの長さを超えることはできません。
配列varでは、配列要素にアクセスするためにoperator->ではなく、次のoperator[]メンバー関数を追加でサポートします。
TYPE_slice& operator[](CORBA::ULong Index);
const TYPE_slice & operator[](CORBA::ULong Index) const;
Index
引数には、戻り値となるスライスの索引を指定します。この索引は、配列のサイズを超えることはできません。
このセクションと「シーケンスvar」で説明するメンバー関数の文字列varには、char *
のTYPEがあります。文字列varでは、次のメンバー関数を追加でサポートします。
String_var(char * str)
String_var(const char * str)
String_var(const String_var & var)
const
文字列からString_var
を作成します。str
引数には、コピー元となるconst文字列を指定します。var
引数には、コピー元となる文字列へのリファレンスを指定します。
String_var & operator=(char * str)
CORBA::string_free
を使用して格納されている文字列を解放してから、入力文字列の所有権を想定します。str
引数には、このString_var
オブジェクトが所有権を想定する文字列を指定します。
String_var & operator=(const char * str)
String_var & operator=(const String_var & var)
CORBA::string_free
を使用して格納されている文字列を解放してから、入力文字列をコピーします。Data
引数には、このString_var
オブジェクトが所有権を想定する文字列を指定します。
char operator[] (Ulong Index)
char operator[] (Ulong Index) const
Index
引数には、配列内の特定の文字にアクセスするときに使用する配列の索引を指定します。索引の基数はゼロです。Char operator[] (Ulong Index)
関数の戻り値は、lvalueとして使用できます。Char operator[] (Ulong Index) const
関数の戻り値は、lvalueとして使用できません。
構造化された型(構造体、ユニオン、シーケンス)、配列、およびインタフェースには、対応する_outクラスが生成されます。outクラスは、可変長型および固定長型へのポインタのメモリー管理を容易にするために提供されます。out
クラスと共通のメンバー関数の詳細は、「outクラスの使い方」を参照してください。
ユーザー定義のデータ型によって、サポートされる演算子は一部異なります。表13-5では、生成されたC++コードにおける各OMG IDLのデータ型でサポートされる様々な演算子について説明しています。表13-5で示すように、代入演算子はすべてのデータ型でサポートされるので、比較には含まれていません。
表13-6では、シグネチャを示します。
TYPE
_var
をout
パラメータとして渡すと、参照先だった前の値はすべて暗黙的に削除する必要があります。の要件を満たす上で十分なフックをORBに付与するために、各T_var
型には対応するTYPE
_out
型があります。この型は、out
パラメータ型としてのみ使用します。
注: | プログラマは、_out クラスを直接インスタンス化できません。そのため、_out クラスは関数のシグネチャでのみ指定してください。 |
可変長型のTYPE
_out
型の全般的な形式は、次のとおりです。
// C++
class TYPE_out
{
public:
TYPE_out(TYPE*& p) : ptr_(p) { ptr_ = 0; }
TYPE_out(TYPE_var& p) : ptr_(p.ptr_) { delete ptr_; ptr_ = 0;}
TYPE_out(TYPE_out& p) : ptr_(p.ptr_) {}
TYPE_out& operator=(TYPE_out& p) { ptr_ = p.ptr_;
return *this;
}
Type_out& operator=(Type* p) { ptr_ = p; return *this; }
operator Type*&() { return ptr_; }
Type*& ptr() { return ptr_; }
Type* operator->() { return ptr_; }
private:
Type*& ptr_;
// assignment from
TYPE
_var not allowed
void operator=(const TYPE_var&):
};
最初のコンストラクタは、リファレンス・データ・メンバーをT*&
引数にバインドし、ポインタをゼロ(0)ポインタ値に設定します。2番目のコンストラクタは、リファレンス・データ・メンバーをTYPE
_var
引数が保持するポインタにバインドし、ポインタに対してdelete
を呼び出します(その際、型がString_out
の場合はstring_free()
を、配列型TYPE
がTYPE
_var
の場合はTYPE
_free()
を呼び出します)。3番目のコンストラクタはコピー・コンストラクタで、コンストラクタ引数のデータ・メンバーによって参照される同じポインタにリファレンス・データ・メンバーをバインドします。
ほかのTYPE
_out
から代入すると、TYPE
_out
引数によって参照されるTYPE
*
がデータ・メンバーにコピーされます。TYPE
*
のオーバーロードの代入演算子は、ポインタ引数をデータ・メンバーに代入するだけです。この意味で、TYPE
_out
型の動作は、TYPE
*
とまったく同じです。TYPE
*&
変換演算子は、データ・メンバーを返します。ptr()
メンバー関数もデータ・メンバーを返しますが、暗黙的な変換の実行を避ける場合に使用できます。オーバーロードの矢印演算子(operator->()
)を使用すると、TYPE
*
データ・メンバーが指すデータ構造体のメンバーにアクセスできます。準拠アプリケーションでは、有効な非NULL
TYPE
*
でTYPE
_out
が初期化済ない限り、オーバーロードのoperator->()
を呼び出すことはできません。
対応するTYPE
_var
型のインスタンスから、TYPE
_out
に代入することはできません。これは、アプリケーション開発者がコピーを行うかどうか、またはTYPE
_var
がTYPE
_out
に代入できるよう、それが管理するポインタの所有権を返すかどうかを判定する方法がないためです。TYPE
_var
をTYPE
_out
にコピーするには、アプリケーションで次のようにnew
を使用する必要があります。
// C++
TYPE
_var t = ...;
my_out = newTYPE
(t.in()); // heap-allocate a copy
通常、t
に対して呼び出されるin()
関数は、const
TYPE
&
を戻して、新しく割り当てられたT
インスタンスのコピー・コンストラクタを呼び出すことができるようにします。
また、TYPE
_var
で、T_out
パラメータで返されるよう、それが管理するポインタの所有権を返すには、アプリケーションで、次のようにTYPE
_var::_retn()
関数を使用する必要があります。
// C++
TYPE
_var t = ...;
my_out = t._retn(); // t yields ownership, no copy
TYPE
_out
型は、アプリケーションで作成または破棄される汎用目的のデータ型としては機能しません。この型は必要なメモリー管理を適切に行う目的で、操作のシグネチャの内部でのみ使用する型です。
_var
をout
パラメータとして渡すと、参照先の前の値はすべて暗黙的に解放する必要があります。この要件を満たす上でC++マッピング実装に十分なフックを付与するために、オブジェクト参照型ごとに_out
型が生成されます。この型は、out
パラメータ型としてのみ使用します。たとえば、インタフェースTYPE
の場合、オブジェクト参照型TYPE
_ptr
、ヘルパー型TYPE
_var
、およびout
パラメータ型TYPE
_out
が生成されます。オブジェクト参照_out
型の全般的な形式は次のとおりです。
// C++
classTYPE
_out
{
public:
TYPE
_out(
TYPE
_ptr& p) : ptr_(p) { ptr_ =
TYPE
::_nil(); }
TYPE
_out(
TYPE
_var& p) : ptr_(p.ptr_) {
release(ptr_); ptr_ =TYPE
::_nil();
}
TYPE
_out(
TYPE
_out& a) : ptr_(a.ptr_) {}
TYPE
_out& operator=(
TYPE
_out& a) {
ptr_ = a.ptr_; return *this;
}
TYPE
_out& operator=(const
TYPE
_var& a) {
ptr_ =TYPE
::_duplicate(
TYPE
_ptr(a)); return *this;
}
TYPE
_out& operator=(TYPE_ptr p) { ptr_ = p; return *this; }
operatorTYPE
_ptr&() { return ptr_; }
TYPE
_ptr& ptr() { return ptr_; }
TYPE
_ptr operator->() { return ptr_; }
private:
TYPE
_ptr& ptr_;
};
シーケンスoutでは、次のoperator[]
メンバー関数を追加でサポートします。
TYPE &operator[](CORBA::ULong Index);
operator[]
を呼び出します。operator[]
は、指定の索引でシーケンスの適切な要素へのリファレンスを返します。Index
引数には、戻り値となる要素の索引を指定します。この索引は、現在のシーケンスの長さを超えることはできません。
配列outでは、配列要素にアクセスするためにoperator->
ではなく、次のoperator[]
メンバー関数を追加でサポートします。
TYPE_slice& operator[](CORBA::ULong Index);
const TYPE_slice & operator[](CORBA::ULong Index) const;
Index
引数には、戻り値となるスライスの索引を指定します。この索引は、配列のサイズを超えることはできません。
String_var
をout
パラメータとして渡すと、参照先の前の値はすべて暗黙的に解放する必要があります。この要件を満たす上でC++マッピング実装に十分なフックを付与するために、文字列型にもCORBAネームスペースでString_out
型が生成されます。この型は、文字列out
パラメータ型としてのみ使用します。String_out
型の全般的な形式は次のとおりです。
// C++
class String_out
{
public:
String_out(char*& p) : ptr_(p) { ptr_ = 0; }
String_out(String_var& p) : ptr_(p.ptr_) {
string_free(ptr_); ptr_ = 0;
}
String_out(String_out& s) : ptr_(s.ptr_) {}
String_out& operator=(String_out& s) {
ptr_ = s.ptr_; return *this;
}
String_out& operator=(char* p) {
ptr_ = p; return *this;
}
String_out& operator=(const char* p) {
ptr_ = string_dup(p); return *this;
}
operator char*&() { return ptr_; }
char*& ptr() { return ptr_; }
private:
char*& ptr_;
// assignment from String_var disallowed
void operator=(const String_var&);
};
最初のコンストラクタは、リファレンス・データ・メンバーをchar*&
引数にバインドします。2番目のコンストラクタは、リファレンス・データ・メンバーをString_var
引数が保持するchar*
にバインドし、文字列に対してstring_free()
を呼び出します。3番目のコンストラクタはコピー・コンストラクタで、その引数のデータ・メンバーにバインドされる同じchar*
に、リファレンス・データ・メンバーをバインドします。
ほかのString_out
から代入すると、String_out
引数によって参照されるchar*
が、データ・メンバーによって参照されるchar*
にコピーされます。char*
のオーバーロードの代入演算子は、char*
引数をデータ・メンバーに代入するだけです。const
char*
のオーバーロードの代入演算子は、引数を複製して結果をデータ・メンバーに代入します。代入を行っても、以前に保持されていた文字列はまったく削除されません。この意味で、String_out
型の動作は、char*
とまったく同じです。char*&
変換演算子は、データ・メンバーを返します。ptr()
メンバー関数もデータ・メンバーを返しますが、暗黙的な変換の実行を避ける場合に使用できます。
String_var
からString_out
への代入はできません。これは、メモリー管理にあいまいさが生じるためです。特に、String_var
によって所有される文字列をString_out
が取得するときに、コピーが行われるか、コピーなしで行われるかどうかを判別することは不可能です。String_var
からの代入が不可能なため、アプリケーション開発者は次のように明示的に指定しなければなりません。
// C++
void
A::op(String_out arg)
{
String_var s = string_dup("some string");
...
out = s; // disallowed; either
out = string_dup(s); // 1: copy, or
out = s._retn(); // 2: adopt
}
コメントで「1」とマークされた行の場合、呼出し側はString_var
が所有する文字列を明示的にコピーし、結果をout
引数に代入しています。また、呼出し側は、コメントで「2」とマークされた行で示すように、String_var
に文字列の所有権を強制的に放棄させるというテクニックも使用できます。これにより、メモリー管理のエラーを発生させずにout
引数に文字列を返すことができます。
パラメータ渡しモードのマッピングでは、効率性と簡便性の両方のバランスを調整します。プリミティブ型、列挙、およびオブジェクト参照の場合、モードは単純で、プリミティブと列挙用の型P
、およびインタフェース型A用の型A_ptr
を渡します。
集約型は複雑です。これは、いつ、どのようにパラメータのメモリーを割り当てるか、または割当て解除するかという問題があるためです。in
パラメータのマッピングは単純です。これは、パラメータのストレージが呼出し側によって割り当てられ、読取り専用であるためです。out
パラメータとinout
パラメータのマッピングは、ほかと比較して問題が多くあります。可変長型の場合、呼出し先はストレージの一部またはすべてを割り当てる必要があります。呼出し側の割当てという点では、3つの浮動小数点値のメンバーを格納する構造体として表されるPoint型など、固定長型の方がスタック割当てが可能なので好ましいと言えます。
両方の割当て方法に対応するために、分割割当てで生じる可能性のある混同を未然に排除します。また、コピーを実行するときに、マッピングで固定長の集約体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ポインタを渡すことができません。
ただし、呼出し側は、out
パラメータのNULL値でポインタへのリファレンスを渡すことはできます。これは、呼出し先が値を確認せずに上書きするためです。呼出し先は、次のいずれかに該当する場合、NULLポインタを渡すことができません。
表13-7には、受け渡しされる型に応じた、基本OMG IDLパラメータ渡しモードおよび戻り値の型のマッピングを示します。表13-8には、T_var
型の場合の同様の情報を示します。表13-8は、あくまで参考用に提供されています。これは、T_out型がすべてのout
パラメータの実際のパラメータ型として使用される点を除けば、クライアントとサーバーの操作のシグネチャが、表13-7
で示すパラメータ渡しモードでコーディングされるためです。
また、T_var
型では、この型を直接渡すのに必要な変換演算子をサポートします。呼出し側は、T_var
型または表13-7で示すベース・タイプのインスタンスを常に渡す必要があります。呼出し先は、そのT_out
パラメータが実際に表13-7の対応する、基底の型であるように扱う必要があります。
表13-7の中で、固定長の配列が適用対象となるのは、out
パラメータの型が戻り値と異なる場合のみです。
これが必要となるのは、C++では関数で配列を返すことができないためです。マッピングは、配列のスライスへのポインタを返します。
配列のスライスとは、指定された元の配列の最初のサイズを除いたすべてのサイズを持つ配列のことです。
注: | データ型object reference ptrには、擬似オブジェクト参照も含まれています。配列スライスの戻り値は、元の配列の最初のサイズを除いたすべてのサイズを持つ配列です。 |
呼出し側は、in
引数として渡される引数すべてにストレージを提供する必要があります。
注: | データ型object reference varには、擬似オブジェクト参照も含まれています。 |
表13-9および表13-10では、inout
パラメータとout
パラメータに関連付けられたストレージ、および戻り値の結果に対する呼出し側の作業について説明します。
1具体的には、関数名およびデータ型として使用されるexception
。