bea ホーム | 製品 | dev2dev | support | askBEA |
|
e-docs > Tuxedo > Tuxedo CORBA プログラミング・リファレンス > OMG IDL 文の C++ へのマッピング |
Tuxedo CORBA プログラミング・リファレンス |
OMG IDL 文の C++ へのマッピング
この章では、OMG IDL 文から C++ へのマッピングについて説明します。
注記 この章の一部の情報は、Object Management Group (OMG) の「Common Object Request Broker: C++ Language Mapping Specification」(1999 年 6 月) から、OMG の許可を得て転載しています。
マッピング
以下では、OMG IDL から C++ へのマッピングの次のトピックについて説明します。
また、次のトピックについても説明します。
データ型
各 OMG IDL のデータ型は、C++ のデータ型またはクラスにマッピングされます。
基本データ型
表 0-1 に示すように、OMG IDL 文の基本データ型は、CORBA モジュールの C++ typedef にマッピングされます。
注記 長精度型 (long) が 64 ビットのマシンでも、CORBA::Long の定義は 32 ビット整数を参照します。 複雑なデータ型 表 0-2 に、オブジェクト、擬似オブジェクト、およびユーザ定義型のマッピングを示します。
文字列および UDT のマッピングの詳細については、以降の節を参照してください。 文字列 OMG IDL の文字列は、C++ の char * にマッピングされます。char * には、バウンディッド文字列とアンバウンディッド文字列の両方がマッピングされます。C++ の CORBA 文字列は、NULL で終了し、char * 型の使用時には常に使用できます。 struct など、ほかのユーザ定義の型の中に文字列がある場合は、CORBA::String_var 型にマッピングされます。これにより、構造体内の各メンバは自身のメモリを管理できます。 文字列の割り当ておよび割り当て解除には、CORBA クラスの次のメンバ関数を使用する必要があります。
注記 string_alloc 関数によって len+1 文字が割り当てられるので、結果として得られた文字列には後続の NULL 文字を保持するのに十分なスペースを確保できます。
wchar
OMG IDL では、任意の文字セットからワイド文字を符号化する wchar データ型を定義します。文字データと同じく、インプリメンテーションでは任意のコード・セットを使用してワイド文字を符号化します。ただし、転送にはほかの形式への変換が必要になる場合もあります。wchar のサイズは、インプリメンテーションによって異なります。
次に、wchar を定義する構文を示します。
<wide_char_type> ::= “wchar”
次に、wchar のコード例を示します。
wchar_t wmixed[256];
注記 wchar および wstring データ型を使用すると、ユーザが記述するネイティブ言語でコンピュータとやり取りできます。日本語や中国語など、言語によっては固有の文字が多数ある言語もあります。このような文字セットは、1 バイトの中に収まりません。マルチ・バイト文字セットをサポートするためにさまざまなスキーマが考案されていますが、まだ実用的なレベルには至っていません。ワイド文字とワイド文字列を使用すると、こういった複雑な文字セットとのやり取りが容易になります。
wstring
wstring データ型は、ワイド文字 NULL を除き wchar のシーケンスを表します。wstring 型は string 型と似ていますが、その要素型が char ではなく wchar である点で異なります。wstring の実際の長さは実行時に設定されます。バウンディッド形式を使用する場合、長さはバウンド以下でなければなりません。
次に、wstring を定義する構文を示します。
<wide_string_type> ::= “wstring” “<” <positive_int_const> “>”
| “wstring
次に、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++ にマッピングされます。
// 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;
Enum
OMG IDL の enum は、C++ の enum にマッピングされます。次に、OMG IDL 定義の例を示します。
// OMG IDL
module INVENT
{
enum Reply {ACCEPT, REFUSE};
}
上の定義は、次のように C++ にマッピングされます。
// C++
class INVENT
{
. . .
enum Reply {ACCEPT, REFUSE};
};
次に、前の例で定義した enum への有効なリファレンスの例を示します。enum へのリファレンスは次のとおりです。
INVENT::Reply accept_reply;
accept_reply = INVENT::ACCEPT;
構造体
OMG IDL の構造体は、C++ の構造体にマッピングされます。
構造体に対して生成されたコードは、固定長か可変長かによって異なります。固定長型と可変長型の詳細については、「固定長ユーザ定義型と可変長ユーザ定義型」を参照してください。
固定長構造体と可変長構造体
可変長構造体には、代入演算子メンバ関数が別にあり、2 つの可変長構造体間の代入を処理します。
次に、OMG IDL 定義の例を示します。
// 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++ にマッピングされます。
// 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 などの基本データ型 については、表 0-1 を参照してください。オブジェクト・リファレンス、擬似オブジェクト・リファレンス、および文字列については、メンバは次の適切な var クラスにマッピングされます。
その他のデータ型については、表 0-2 を参照してください。
生成される構造体にはコンストラクタがないため、メンバは初期化されません。固定長構造体の場合、集約初期化を行うと初期化できます。たとえば、次のように入力します。
INVENT::Date a_date = { 1995, 10, 12 };
可変長メンバの場合、自己管理型にマッピングします。この型には、メンバを初期化するコンストラクタが含まれています。
Var
構造体に対して var クラスが生成されます。詳細については、var クラスの使い方を参照してください。
Out
構造体に対して out クラスが生成されます。詳細については、out クラスの使い方を参照してください。
共用体
OMG IDL の共用体は、C++ のクラスにマッピングされます。C++ クラスには、次のものが格納されています。
次に、OMG IDL 定義の例を示します。
// OMG IDL
union OrderItem switch (long)
{
case 1: itemStruct itemInfo;
case 2: orderStruct orderInfo;
default: ID idInfo;
};
上の定義は、次のように C++ にマッピングされます。
// 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 番目の関数 (アクセサ) では、共用体の値を返しています。
共用体メンバのデータ型によっては、モディファイア関数が追加生成されます。各データ型に対して生成されるメンバ関数は次のとおりです。
Var
共用体に対して var クラスが生成されます。詳細については、var クラスの使い方を参照してください。
Out
共用体に対して out クラスが生成されます。詳細については、out クラスの使い方を参照してください。
メンバ関数
アクセサとモディファイアのほかに、型 TYPE の OMG IDL 共用体に対して switch (long) 区別子で次のメンバ関数が生成されます。
シーケンス
OMG IDL のシーケンスは、C++ のクラスにマッピングされます。C++ クラスには、次のものが格納されています。
各シーケンスには、次のものがあります。
要素にアクセスする場合、事前に長さを設定しておく必要があります。
次に、OMG IDL 定義の例を示します。
// OMG IDL
module INVENT
{
. . .
typedef sequence<LogItem> LogList;
}
上の定義は、次のように C++ にマッピングされます。
// 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++ の適切なデータ型にマッピングされます。
基本データ型については、表 0-1 を参照してください。オブジェクト・リファレンス、TypeCode リファレンス、および文字列の場合、基本型は生成された _ForSeq_var クラスにマッピングされます。_ForSeq_var クラスは、文字列またはシーケンス内に格納されたオブジェクトを更新する機能を備えています。このクラスには、対応する var クラスと同じメンバ関数とシグニチャがあります。ただし、_ForSeq_var クラスは、シーケンス・コンストラクタの release パラメータの設定に従います。ほかのクラスとの相違点は、_ForSeq_var クラスの場合、ユーザが Release フラグを指定できるので、メモリの解放を制御できることです。
その他のデータ型については、表 0-2 を参照してください。
Vars
シーケンスに対して var クラスが生成されます。詳細については、var クラスの使い方を参照してください。
Out
シーケンスに対して out クラスが生成されます。詳細については、out クラスの使い方を参照してください。
メンバ関数
以下では、基本型が TYPE で指定の OMG IDL シーケンスが SEQ の場合に、生成されるシーケンス・クラスのメンバ関数について説明します。
CORBA::Boolean Release);
const TYPE & operator[](CORBA::ULong Index) const;
配列
OMG IDL の配列は、C++ の 配列定義にマッピングされます。次に、OMG IDL 定義の例を示します。
// OMG IDL
module INVENT
{
. . .
typedef LogItem LogArray[10];
};
上の定義は、次のように C++ にマッピングされます。
// 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 定義の例を示します。
// OMG IDL
typedef LogItem LogMultiArray[5][10];
上の定義は、次のように C++ にマッピングされます。
// C++
typedef LogItem LogMultiArray[5][10];
typedef LogItem LogMultiArray_slice[10];
配列のサイズが 1 つの場合、配列スライスは型だけになります。たとえば、サイズが 1 つの long の配列がある場合、配列スライスは CORBA::Long データ型になります。
配列要素のマッピング
OMG IDL の配列の型は、構造体と同じ方法で C++ の配列要素型にマッピングされます。詳細については、メンバのマッピングを参照してください。
Var
配列に対して var クラスが生成されます。詳細については、var クラスの使い方を参照してください。
Out
配列に対して out クラスが生成されます。詳細については、out クラスの使い方を参照してください。
割り当てメンバ関数
各配列には、配列の割り当ておよび割り当て解除を行う 2 つの静的関数があります。指定の OMG IDL の型 TYPE の場合、割り当てルーチンと割り当て解除ルーチンは次のとおりです。
例外
OMG IDL の例外は、C++ のクラスにマッピングされます。C++ クラスには、次のものが格納されています。
生成されるクラスは可変長構造体と似ています。ただし、このクラスは初期化を簡単にするためのコンストラクタ、および UserException の型を判別するための静的 _narrow メンバ関数が追加されている点で異なります。
次に、OMG IDL 定義の例を示します。
// OMG IDL
module INVENT
{
exception NonExist
{
ID BadId;
};
};
上の定義は、次のように C++ にマッピングされます。
// 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 クラスが生成されます。詳細については、var クラスの使い方を参照してください。
Out
例外に対して out クラスが生成されます。詳細については、out クラスの使い方を参照してください。
メンバ関数
指定の OMG IDL 例外 TYPE の場合、生成されるメンバ関数は次のとおりです。
擬似オブジェクトの C++ へのマッピング
CORBA 擬似オブジェクトは、通常の CORBA オブジェクトまたはサーバレス・オブジェクトとしてインプリメントできます。CORBA 仕様における、両者の基本的な違いは次のとおりです。
サーバレス・オブジェクトへのリファレンスは、アドレス空間などのコンピュータ間のコンテキストで有効とは限りません。代わりに、パラメータとして渡されるサーバレス・オブジェクトへのリファレンスでは、このリファレンスの受信側が使用するオブジェクトの、非依存的で機能的に同じコピーが作成されます。これをサポートするには、データ・レイアウトなど、サーバレス・オブジェクトのほかの隠れた表現プロパティを ORB に認識させます。この章では、ORB にサーバレス・オブジェクトを認識させる仕様については、インプリメンテーション上の詳細事項のため説明は割愛します。
この章では、代わりにすべての擬似オブジェクト型の標準的なマッピング・アルゴリズムについて説明します。これにより、9 種類の CORBA 擬似オブジェクト型ごとのマッピングについて、説明が断片的にならずに済み、CORBA の将来のリビジョンで提供される擬似オブジェクト型にも対応できます。また、C マッピングの表現に依存せずに、C 互換の表現に依存したインプリメンテーションを実現することができます。
形式
C PIDL とは異なり、このマッピングでは、完全な OMG IDL の拡張形式を使用してサーバレス・オブジェクト型を記述します。擬似オブジェクト型のインターフェイスでは、通常の OMG IDL インターフェイスと同じ規則に従います。ただし、次の例外があります。
pseudo 接頭辞は、そのインターフェイスが通常の方法またはサーバレスの方法のどちらかでインプリメントされることを示します。つまり、以降の節で説明する規則か、またはこの節で説明する通常のマッピング規則のどちらかを適用することになります。
マッピング規則
サーバレス・オブジェクトは、この節で概説する相違点を除いて、通常のインターフェイスと同じ方法でマッピングされます。
サーバレス・オブジェクト型を表すクラスは、CORBA::Object のサブクラスではありません。また、ほかの C++ クラスのサブクラスである必要はありません。したがって、Object::create_request などのオペレーションを必ずしもサポートしません。
サーバレス・オブジェクト型の T を表す各クラスの場合、次の関数のオーバーロードの各バージョンが CORBA 名前空間で提供されます。
// C++
void release(T_ptr);
Boolean is_nil(T_ptr p);
サブクラスはインプリメンテーションで提供できます。ただし、マッピングされた C++ クラスは、ユーザが容易にサブクラス化できるという保証はありません。インプリメンテーションでは、サブクラスに適用されない内部表現や転送形式を想定することは可能です。
サーバレス・オブジェクト型を表すクラスのメンバ関数は、通常のメモリ管理規則に従うとは限りません。これは、CORBA::NVList などの一部のサーバレス・オブジェクトが、本質的にほかのサーバレス・オブジェクトのいくつかのレベルのコンテナでしかない場合があるためです。格納されたサーバレス・オブジェクトのアクセサ関数から返された値を、呼び出し側が明示的に解放しなければならないということは、本来の目的とは反対の使い方になってしまいます。
マッピングのその他の要素はすべて、次に示すように同じです。
// C++
static T_ptr _duplicate(T_ptr p);
static T_ptr _nil();
C PIDL マッピングとの関係
サーバレス・オブジェクトのインターフェイスと宣言がこれらに依存する場合、すべて C マッピングに直接類似しています。マッピングされた C++ クラスは、必要に応じて、C マッピング用に選択したクラスと互換性のある表現を使用してインプリメントすることができます。C PIDL と C++ PIDL の擬似オブジェクト仕様の相違点は次のとおりです。
以降の節では、各擬似インターフェイスとその C++ マッピングの概説と一覧を示します。以下で定義が行われない型も含め、詳細については、このマニュアルの関連する節を参照してください。
Typedef
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 定義の例を示します。
// OMG IDL
module INVENT
{
interface Order
{
. . .
ItemList modifyOrder (in ItemList ModifyList);
};
};
上の定義は、次のように C++ にマッピングされます。
// 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)
{
. . .
}
引数のマッピング
オペレーションの各引数は、表 0-1 および 表 0-2 で説明した、対応する C++ の型にマッピングされます。
オペレーションでの引数のパラメータ渡しモードについては、表 0-7 および 表 0-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(); // オブジェクト・リファレンスを取得
PortableServer 関数
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 定義の例を示します。
// OMG IDL
module INVENT
{
interface Order
{
. . .
};
};
上の定義は、次のように C++ にマッピングされます。
// C++
class INVENT
{
. . .
class Order : public virtual CORBA::Object
{
.. .
}; // Order クラス
}; // INVENT クラス
入れ子になったモジュールが複数ある場合は、複数の入れ子になったクラスが生成されます。モジュール内部にあるものはすべてモジュール・クラスの中にあり、インターフェイス内部にあるものはすべてインターフェイス・クラスの中にあります。
OMG IDL では、モジュール、インターフェイス、および型の名前を同じにすることができます。ただし、C++ 言語用のファイルを生成する場合、名前を同じにすることはできません。この制限が必要になるのは、OMG IDL の名前が同じ名前で入れ子の C++ クラスに生成される際に、これを C++ コンパイラでサポートしていないためです。
注記 現在のモジュール名と同じ名前のインターフェイスまたは型で OMG IDL から C++ コードを生成すると、BEA Tuxedo OMG IDL コンパイラでは、その情報を伝えるメッセージが出力されます。このメッセージを無視して、インターフェイスまたは型とモジュール名とを区別するために一意の名前を付けなかった場合、コンパイラによってファイルの生成時にエラーが発生したことが通知されます。
インターフェイス
OMG IDL のインターフェイスは、C++ のクラスにマッピングされます。このクラスは、OMG IDL インターフェイス内にあるオペレーション、属性、定数、およびユーザ定義の型 (UDT) を格納します。
インターフェイス INTF の場合、生成されるインターフェイス・コードには、次の項目が格納されます。
次に、OMG IDL 定義の例を示します。
// OMG IDL
module INVENT
{
interface Order
{
void cancelOrder ();
};
};
上の定義は、次のように C++ にマッピングされます。
// 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 の各静的メンバ関数について詳細に説明します。
オブジェクト・リファレンスの型
インターフェイス・クラス (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++ にマッピングされます。
// 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++ の型については、表 0-1 および表 0-2 を参照してください。オペレーションでの引数のパラメータ渡しモードについては、表 0-7 および 表 0-8 を参照してください。
Any 型
OMG IDL の any は、CORBA::Any クラスにマッピングされます。CORBA::Any クラスは、型セーフな方法で C++ の型を処理します。
型付き値の処理
一致しない TypeCode や値で any が作成される可能性を低くするには、C++ 関数のオーバーロード機能を使用します。具体的には、OMG IDL 仕様の特定の型ごとに、その型の値を挿入および抽出するためのオーバーロード関数が用意されています。名前空間の純粋さが損なわれるのを完全に防ぐには、これらの関数にオーバーロード演算子を使用します。これらの関数については以下で詳細に説明しますが、特徴としては、any に挿入または any から抽出される値の C++ の型によって適切な TypeCode が示されるという点です。
後述する型セーフな any インターフェイスは C++ 関数のオーバーロードに基づいているため、OMG IDL 仕様から生成される C++ の型は別々のものである必要があります。ただし、この要件が満たされない特別な場合があります。
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 の値を設定することは、次のことを意味します。
文字列型の挿入をコピーすると、次の関数が呼び出されます。
// 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 からの抽出
型セーフな方法で 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;
};
上記の構造体は、次のように Any から抽出できます。
// 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 型を使用します。この型については、「配列」を参照してください。
次に、OMG IDL の例を示します。
// 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 型でもサポートされます。
Boolean、Octet、Char、およびバウンディッド文字列の識別
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); // この行はコンパイルされる。
Object への型の拡大
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 ポインタを返します。
Any のコンストラクタ、デストラクタ、代入演算子
デフォルト・コンストラクタは、値を作成せずに、型tk_null の TypeCode で Any を作成します。コピー・コンストラクタは、Any パラメータの TypeCode_ptr で _duplicate を呼び出し、パラメータの値をディープ・コピーします。代入演算子は、それ自身の TypeCode_ptr を解放し、必要に応じて現在の値の記憶域を割り当て解除します。その後、Any パラメータの TypeCode_ptr を複製し、パラメータの値をディープ・コピーします。デストラクタは、TypeCode_ptr で release を呼び出し、必要に応じて値の記憶域を割り当て解除します。
その他のコンストラクタについては、「型付けされていない値の処理」を参照してください。
Any クラス
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 オブジェクトのインスタンスが破棄されたり、新しい値が var オブジェクトに割り当てられたときにメモリを解放します。
BEA 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 const TYPE_ptr&() const;
operator TYPE_ptr&();
operator TYPE_ptr;
};
次に、各メンバ関数の詳細について説明します。
TYPE *operator->() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
ユーザ定義のデータ型によって、サポートされる演算子は一部異なります。表 0-3 では、生成された C++ コードにおける各 OMG IDL のデータ型でサポートされるさまざまな演算子について説明しています。表 0-3 で示すように、代入演算子はすべてのデータ型でサポートされるので、比較には含まれていません。
シーケンス var シーケンス var では、次の operator[] メンバ関数を追加でサポートします。
配列 var
配列 var では、配列要素にアクセスするために operator-> ではなく、次の operator[] メンバ関数を追加でサポートします。
const TYPE_slice & operator[](CORBA::ULong Index) const;
文字列 var
この節と「シーケンス var」で説明するメンバ関数の文字列 var には、char * の TYPE があります。文字列 var では、次のメンバ関数を追加でサポートします。
String_var(const String_var & var)
String_var & operator=(const String_var & var)
char operator[] (Ulong Index) const
Char operator[] (Ulong Index) const 関数の戻り値は、lvalue として使用できません。
out クラス
構造化された型 (構造体、共用体、シーケンス)、配列、およびインターフェイスには、対応する _out クラスが生成されます。out クラスは、可変長型および固定長型へのポインタのメモリ管理を容易にするために提供されます。out クラスと共通のメンバ関数の詳細については、「out クラスの使い方」を参照してください。
ユーザ定義のデータ型によって、サポートされる演算子は一部異なります。表 0-5 では、生成された C++ コードにおける各 OMG IDL のデータ型でサポートされるさまざまな演算子について説明しています。表 0-3 で示すように、代入演算子はすべてのデータ型でサポートされるので、比較には含まれていません。
out クラスの使い方
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 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 = new TYPE(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 型は、アプリケーションで作成または破棄される汎用目的のデータ型としては機能しません。そのため、この型は必要なメモリ管理を適切に行う目的で、オペレーションのシグニチャの内部でのみ使用する型です。
オブジェクト・リファレンスの out パラメータ
_var を out パラメータとして渡すと、参照先の前の値はすべて暗黙的に解放する必要があります。この要件を満たす C++ マッピング・インプリメンテーションのフックを付与するために、オブジェクト・リファレンス型ごとに _out 型が生成されます。この型は、out パラメータ型としてのみ使用します。たとえば、インターフェイス TYPE の場合、オブジェクト・リファレンス型 TYPE_ptr、ヘルパ型 TYPE_var、および out パラメータ型 TYPE_out が生成されます。オブジェクト・リファレンス _out 型の一般的な形式は次のとおりです。
// C++
class TYPE_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; }
operator TYPE_ptr&() { return ptr_; }
TYPE_ptr& ptr() { return ptr_; }
TYPE_ptr operator->() { return ptr_; }
private:
TYPE_ptr& ptr_;
};
シーケンス out
シーケンス out では、次の operator[] メンバ関数を追加でサポートします。
配列 out
配列 out では、配列要素にアクセスするために operator-> ではなく、次の operator[] メンバ関数を追加でサポートします。
const TYPE_slice & operator[](CORBA::ULong Index) const;
文字列 out
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 が渡されるときにパラメータ内の以前の可変長データをすべて割り当て解除する必要があります。これらのパラメータの初期値がオペレーションに送信されない場合でも、BEA 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); // 最初の結果は解放
S *sp; // out に渡す前の初期化は不要
f(sp);
// use 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 ポインタを渡すことができません。
オペレーションのパラメータおよびシグニチャ
表 0-7 には、受け渡しされる型に応じた、基本 OMG IDL パラメータ渡しモードおよび戻り値の型のマッピングを示します。表 0-8 には、T_var 型の場合の同様の情報を示します。表 0-8 は、あくまで参考用に提供されています。これは、T_out 型がすべての out パラメータの実際のパラメータ型として使用される点を除けば、クライアントとサーバのオペレーションのシグニチャが、表 0-7 で示すパラメータ渡しモードでコーディングされるためです。
また、T_var 型では、この型を直接渡すのに必要な変換演算子をサポートします。呼び出し側は、T_var 型または表 0-7 で示す基本型のインスタンスを常に渡す必要があります。呼び出し先は、その T_out パラメータが実際に 表 0-7 の対応する、基となる型であるように扱う必要があります。
表 0-7 の中で、固定長の配列が適用対象となるのは、out パラメータの型が戻り値と異なる場合のみです。これが必要となるのは、C++ では関数で配列を返すことができないためです。
マッピングは、配列のスライスへのポインタを返します。
配列のスライスとは、指定された元の配列の最初のサイズを除いたすべてのサイズを持つ配列のことです。
注記 データ型 object reference ptr には、擬似オブジェクト・リファレンスも含まれています。配列スライスの戻り値は、元の配列の最初のサイズを除いたすべてのサイズを持つ配列です。 呼び出し側は、in 引数として渡される引数すべてに記憶域を提供する必要があります。
注記 データ型 object reference var には、擬似オブジェクト・リファレンスも含まれています。 表 0-9 および 表 0-10 では、inout パラメータと out パラメータに関連付けられた記憶域、および戻り値の結果に対する呼び出し側の作業について説明します。
型 inout パラメータ out パラメータ 戻り値の結果 short 1 1 1 long 1 1 1 unsigned short 1 1 1 unsigned long 1 1 1 float 1 1 1 double 1 1 1 boolean 1 1 1 char 1 1 1 wchar 1 1 1 octet 1 1 1 enum 1 1 1 object reference ptr 2 2 2 struct (固定長) 1 1 1 struct (可変長) 1 3 3 union (固定長) 1 1 1 union (可変長) 1 3 3 string 4 3 3 wstring 4 3 3 sequence 5 3 3 array (固定長) 1 1 6 array (可変長) 1 6 6 any 5 3 3