|
この章では、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
{
// 固定長
struct Date
{
long year;
long month;
long day;
};
// 可変長
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); // メンバー「s」を選択
st = u.s(); // st == 1296
u._d(2); // OK : メンバー「s」がまだ選択されている
st = u.s(); // st == 1296
u._d(3); // BAD_PARAM : 別のメンバーを選択
_d()
モディファイアが呼び出されると、Tuxedo C++ の「暗黙的な切替」の制約が緩和されます。この場合、例外は送出されず、ユニオンへの影響はありません。
U u2;
u2._d(1); // 例外なし、ユニオンの変更もなし
st = u2.s(); // error! accessing an uninitialized union
u2.it(1296); // OK : これでメンバー「it」が選択された
CORBA::Long _d () const;
OMG IDL のシーケンスは、C++ のクラスにマッピングされます。C++ クラスには、次のものが格納されています。
要素にアクセスする場合、事前に長さを設定しておく必要があります。
// OMG IDL
module INVENT
{
.. .
typedef sequence<LogItem> LogList;
}
// C++
class LogList
{
public:
// デフォルト コンストラクタ
LogList();
// 最大コンストラクタ
LogList(CORBA::ULong _max);
// TYPE * データ コンストラクタ
LogList
(
CORBA::ULong _max,
CORBA::ULong _length,
LogItem *_value,
CORBA::Boolean _relse = CORBA_FALSE
);
// コピー コンストラクタ
LogList(const LogList&);
// デストラクタ
~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;
UDT は 3 つの typedef にマッピングされます。たとえば、次のように入力します。
// 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);
};
生成されたクライアント アプリケーション スタブには、スタブ クラスに対して生成された次のコードが格納されます。
// ルーチン名 : INVENT::Stub_Order::modifyOrder
//
// 関数の説明 :
//
// オペレーション 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; // A の実装を宣言
A_ptr a = &my_a; // C++ 派生ベースの規則で
// そのオブジェクト参照を取得
上のようなコードは、ORB 準拠の実装でサポートしていますが、必須ではありません。そのため、移植性はありません。移植性のある同等のコードでは、実装オブジェクトで _this()
を呼び出し、未登録の場合は暗黙的にそれを登録して、そのオブジェクト参照を取得します。
// C++
MyImplOfA my_a; // A の実装を宣言
A_ptr a = my_a._this(); // オブジェクト参照を取得
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
{
.. .
}; // Order クラス
}; // 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);
};
生成されたクライアント アプリケーション スタブには、スタブ クラスに対して生成された次のコードが格納されます。
// ルーチン名 : INVENT::Stub_Item::itemInfo
//
// 関数の説明 :
//
// 属性 INVENT::Stub_Item::itemInfo の
// クライアント アプリケーション スタブ ルーチン (Interface : Item)
INVENT::itemStruct * INVENT::Stub_Item::itemInfo ( )
{
. ..
}
//
// ルーチン名 : INVENT::Stub_Item::itemInfo
//
// 関数の説明 :
//
// 属性 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&); // コピー形式
void operator<<=(Any&, T*); // 非コピー形式
コピー形式は、呼び出し側に関して見れば、最初に示した形式とほぼ同じです。
上記の「左シフト代入」演算子は、次のように型付き値を 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;
// ... s を初期化 ...
Any a;
a <<= s; // 行 1
a <<= SA_forany(s); // 行 2
行 1 の場合、配列型 SA
がその最初の要素へのポインタに集束されるため、コピー SA_forany
挿入演算子ではなく、非コピー operator<<=(Any&, S*)
が呼び出されます。行 2 の場合、SA_forany
型が明示的に作成されるので、目的の挿入演算子が呼び出されます。
オブジェクト参照の operator<<=
の非コピー バージョンでは、次のように T_ptr
型のアドレスを取得します。
// IDL
interface T { ...};
// C++
void operator<<=(Any&, T_ptr); // コピー
void operator<<=(Any&, T_ptr*); // 非コピー
非コピーのオブジェクト参照の挿入では、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) {
// ... 値を使用 ...
}
この場合、型 Long
の operator>>=
のバージョンは、Any が型 Long
の値を格納しているかどうかを判別します。格納している場合、呼び出し側によって提供されたリファレンス変数にその値をコピーし、TRUE
を返します。Any が型 Long
の値を格納していない場合、呼び出し側のリファレンス変数の値は変更されず、operator>>=
は FALSE
を返します。
型が非プリミティブの場合、ポインタが抽出を行います。次に、OMG IDL 構造体の例を示します。
// IDL
struct MyStruct {
long lmem;
short smem;
};
// C++
Any a;
// ... a に型 MyStruct の値を指定 ...
MyStruct *struct_ptr;
if (a >>= struct_ptr) {
// ... 値を使用 ...
}
抽出に成功した場合、呼び出し側のポインタは 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&); // 型 A 用
Boolean operator>>=(const Any&, B_forany&); // 型 B 用
Array_forany
型は、リファレンスによって常に operator>>=
に渡されます。
文字列および配列の場合、アプリケーションは Any の TypeCode をチェックします。これは、抽出値を使用する際に、アプリケーションが確実に配列オブジェクトまたは文字列オブジェクトのバウンドを超えないようにするためです。
operator>>=
は、Any_var
型でもサポートされます。
OMG IDL の型の Boolean、octet、および char は、特定の C++ 型にマッピングする必要はありません。そのため、型保障 Any インタフェースで使用できるようにするために、これらの型を互いに識別する方法が別に必要となります。同様に、バウンディッド文字列とアンバウンディッド文字列は共に char*
にマッピングされるため、両者を識別する方法が別に必要となります。ここでは、識別を行うために、Any クラス インタフェース内でネストされた新しいヘルパー型をいくつか導入します。ヘルパー型を使用した識別の例を次に示します。
// C++
class Any
{
public:
// 特別なヘルパー型が boolean、octet、char、
// およびバウンディッド文字列の挿入に必要
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);
// 特別なヘルパー型が boolean、octet、char、
// およびバウンディッド文字列の抽出に必要
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;
// ほかのパブリック Any の詳細は省略
private:
// 下記の関数はプライベートで実装されない。
// 下記の関数を隠ぺいすると、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)) {
// ... Boolean を格納した any ...
}
char* p = "bounded";
any <<= Any::from_string(p, 8);
// ...
if (any >>= Any::to_string(p, 8)) {
// ... string<8> を格納した any ...
}
バウンド値が 0 (ゼロ) の場合、アンバウンディッド文字列を示します。
Any へのバウンディッド文字列またはアンバウンディッド文字列の挿入をコピーせずに行う場合、from_string
コンストラクタで nocopy
フラグを TRUE
に設定する必要があります。
// C++
char* p = string_alloc(8);
// ... 文字列 p を初期化 ...
any <<= Any::from_string(p, 8, 1); // any は p を処理
boolean、char、および octet がすべて C++ 型の unsigned
char
にマッピングされる場合、boolean、char、または octet 型の any の直接挿入または抽出を試行すると、unsigned
char
用のプライベートで実装されていない operator<<=
と operator>>=
関数によって、コンパイル エラーが発生します。
// C++
Octet oct = 040;
Any any;
any <<= oct; // この行はコンパイルされない
any <<= Any::from_octet(oct); // この行はコンパイルされる
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 ポインタでもかまいません。
Any クラスでは、3 つの危険なオペレーションも定義します。
// 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 アーキテクチャへの重要な追加機能であると言えます。リファレンスで渡されるオブジェクトと同じく、値型には状態とメソッドがありますが、オブジェクト参照はありません。また、プログラミング言語のオブジェクトと同様に常にローカルで呼び出されます。受信側から要求があると、値型は送信コンテキストでその状態をパッケージ化し、「ネットワーク経由で」状態を受信側に送信します。受信側では、インスタンスが作成され、転送された状態が設定されます。この段階以降、送信側はクライアントサイドのインスタンスを制御できなくなります。つまり、これ以降は、受信側がインスタンスをローカルで呼び出すことができるようになります。このモデルにより、ネットワーク経由の通信で生じる遅延を短縮することができます。この遅延は、大規模なネットワークで顕著に生じます。値型を追加することにより、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;
};
上記の値型の C++ へのマッピングは次のとおりです。
// 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:
// プライベートおよび未実装
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:
// コンストラクタ
TYPE_var();
TYPE_var(TYPE *);
TYPE_var(const TYPE_var &);
// デストラクタ
~TYPE_var();
// 代入演算子
TYPE_var &operator=(TYPE *);
TYPE_var &operator=(const TYPE_var &);
// アクセサ演算子
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-3 で示すように、代入演算子はすべてのデータ型でサポートされるので、比較には含まれていません。
表 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_;
//
TYPE
_var からの代入は不可
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()); // コピーのヒープ割り当て
通常、t
で呼び出される in()
関数は、const
TYPE
&
を返して、新しく割り当てられた T
インスタンスのコピー コンストラクタを呼び出すことができるようにします。
また、TYPE
_var
で、T_out
パラメータで返されるよう、それが管理するポインタの所有権を返すには、アプリケーションで、次のように TYPE
_var::_retn()
関数を使用する必要があります。
// C++
TYPE
_var t = ...;
my_out = t._retn(); // t は所有権を返す。コピーは行われない
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_;
// String_var からの代入は不可
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; // 不可、次のどちらかで指定
out = string_dup(s); // 1 : コピー、または
out = s._retn(); // 2 : 借用
}
コメントで「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);
// s を使用
f(s); // 最初の結果は解放
S *sp; // out に渡す前の初期化は不要
f(sp);
// sp を使用
delete sp; // 次の呼び出しで古い値が解放されることは想定不可能
f(sp);
out
パラメータと inout
パラメータの前の値を暗黙的に割り当て解除できるのは、T_var
型だけです。
// IDL
void q(out string s);
// C++
char *s;
for (int i = 0; i < 10; i++)
q(s); // メモリ リーク
ループ内で q
関数を呼び出すたびに、メモリ リークが発生します。これは、呼び出し側が out
結果で string_free
を呼び出していないことが原因です。これを修正するには、次に示す 2 つの方法があります。
// C++
char *s;
String_var svar;
for (int i = 0 ; i < 10; i++) {
q(s);
string_free(s); // 明示的な割り当て解除
// または :
q(svar); // 暗黙的な割り当て解除
}
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
。