1.2.2.12. C++マッピング

IDL言語のそれぞれの構成要素をC++言語で使用する規則を定めているのがC++言語マッピングです。本書ではC++マッピングについて説明します。
スコープを持つ名前(ScopedNames)
IDLのスコープを持つ名前はC++のスコープに対応づけられます。
モジュール(module)
次のようなIDLは
//IDL
module M {
       interface I {
                struct S {
                ...
                };
       ...
       };
};
C++では次のような宣言になります。
//C++
namespace M
{
       class I ...
       {
       ...
                struct S
                {
                ...
                };
       };
};

インタフェース
IDLのinterfaceはinterface中で定義された型、例外、メソッド等が定義されたC++のクラスに対応づけられます。
// IDL
interface I {
       void op1();
};
 
// C++
class I ...
{
       public void op1(...);
};
CORBAに準拠するアプリケーションプログラムでは、次のことを守らなければなりません。
次に例を示します。
// IDL
interface A
{
       struct S { short field; };
};
 
// C++マッピングに準拠した方法
A_S s; // structの変数を宣言
s.field = 3; // フィールドのアクセス
 
// C++マッピングに違反した方法
// interface classを作成してはいけない
A a;
// interface classへのポインタを使用してはいけない
A *p;
// interface classへのリファレンスを使用してはいけない
void f(A &r);

オブジェクトリファレンス型
IDLのインタフェースをあらわすにはオブジェクトリファレンス型を使用します。オブジェクトリファレンス型はインタフェース名をAとするとA_varおよびA_ptrの2種類があります。インタフェースのメソッドを呼び出すときにはオブジェクトリファレンスに矢印オペレータ(->)を使用して呼び出します。たとえば、インタフェースのメソッドop1を呼び出すときにはオブジェクトリファレンスを使用して、obj->op1(..)のように呼び出します。オブジェクトリファレンスとしてA_var型を用いた場合でも、A_ptrを用いた場合でも同様に矢印オペレータを使用します。
 
A_varとA_ptrの違いは、A_varではオブジェクトリファレンスのメモリ管理が自動的に行われることです。

{
       // C++のスコープ
       A_var objvar = ...;
       A_ptr objptr = ...;
       objvar->op(...);
       objptr->op2(...);
}
 
// スコープの外に出るとobjvarの指すオブジェクトは自動的にリリースされるが、
// objptrは有効なまま。したがってobjptrは明示的にリリースしない限り、
// リークしている。
A_varとA_ptr型はお互いに代入可能です。また、A_varが使用できる場所では、A_ptrも同様に使用できますし、逆も同じです。しかし、A_varで管理されるオブジェクトリファレンスは自動的にリリースされることに十分注意してプログラミングする必要があります。次の例は実行時エラーになります。
 
...
A_ptr objptr = ...;
{
       A_var objvar =
objptr;
       objvar->op1(...);
}
 
// objptrの指すオブジェクトリファレンスはA_var objvarが
// スコープを出たときに自動的にリリースされている。
// したがって、ここで呼び出しを行うとリリースされてしまったものを
// 使用して呼び出すことになる。
 
objptr->op1(..); // 実行時エラーになる可能性あり
 

オブジェクトリファレンスの操作

WideningとNarrowing
インタフェースBがインタフェースAを継承している場合、
//IDL
interface A {
       ...
};
 
interface B : A {
       ...
};
次のことが可能です。
// C++
 
A_ptr ap;
B_ptr bp;
B_var bv;
CORBA::Object_ptr objp;
ap = bp;
objp = bp;
ap = bv;
objp = bv;
B_varからA_varやCORBA::Object_varへの暗黙の変換はサポートされていません。この場合には_duplicateを呼び出して相当することを行う必要があります。
ただし、同じ型どうしのvarの代入は可能です。

// C++
B_ptr bp = ...
A_ptr ap = bp; // 暗黙のwidening
CORBA::Object_ptr objp = bp; // 暗黙のwidening
objp = ap; // 暗黙のwidening
B_var bv = bp; // bvがbpの所有権を持つようになる
ap = bv; // 暗黙のwidening。bvはbpの所有権を持ったまま
obp = bv; // 暗黙のwidening。bvはbpの所有権を持ったまま
A_var av = bv; // できない。コンパイルエラー
A_var av = B::_duplicate(bv); // _varのwideningのやり方
A_var a2v;
a2v = av; // OK。自動的に_duplicateが行われる
オブジェクトリファレンスは_narrowというスタティックメンバ関数を持っており、これを使用することにより、Wideningされたオブジェクトリファレンスからサブインタフェースを指すオブジェクトリファレンスを得ることができます。
A_ptr aptr = ...;
 
// 暗黙的にWideningがおこる
CORBA::Object_ptr objptr = aptr;
A_ptr a2ptr;
 
// A::_narrowを呼び出すことにより新たに
// Aのオブジェクトを指すオブジェクトリファレンスが得られる
a2ptr = A::_narrow(objptr);
ここで注意しなければならないのは、_narrowは新たにオブジェクトリファレンスを作成して返すことです(これに対して、Wideningは新たなオブジェクトリファレンスを作成しません)。したがって、返されたオブジェクトリファレンスは元のオブジェクトリファレンスとは別に解放をしなければなりません。ただし、新たに作られるのはオブジェクトリファレンスであって、オブジェクトそのものではありません。どちらのオブジェクトリファレンスを使用してオペレーションの呼び出しを行っても、実際に実行が行われるオブジェクトは同じオブジェクトです。

_narrowを行ったときに、実際にはその型にnarrowできないときには_narrowはnilオブジェクトリファレンスを返します。

// IDL
interface Base {
       ...
};
 
interface Dervied : Base {
       ...
};
 
interface Another {
       ...
};
 
//C++
Derived_ptr d = ...;
Base_ptr b = d; // widening
Derived_ptr d2 = Derived::_narrow(b);
 
// Derivedのオブジェクトリファレンスを返す
Another_ptr anotherptr = ...;
 
// anotherptrはDerivedのベースではないので
// nilオブジェクトリファレンスが返る
Derived_ptr d3 = Derived::_narrow(anotherptr);

オブジェクトリファレンスに対する操作
すべてのオブジェクトリファレンスに対して_duplicate、release、is_nilという操作が定義されています。これらはオブジェクトリファレンスに対する操作であってオブジェクト自体に対する操作ではないことに注意してください。
 
3つの操作のうちrelease、is_nilはオブジェクトリファレンスの型に依存しないので、CORBAモジュール中で定義されています。

// C++
 
CORBA::release(CORBA::Object_ptr obj);
 
CORBA::Boolean CORBA::is_nil(CORBA::Object_ptr obj);
CORBA::releaseはオブジェクトリファレンスを解放します。オブジェクト自体を解放するわけではありません。CORBA::is_nilはオブジェクトリファレンスがnilかどうかをチェックし、nilであれば1を返します。
 
_duplicateはそれぞれのオブジェクトの型ごとにスタティックメンバ関数として用意されています。_duplicateは引数で与えられたオブジェクトリファレンスから、同じオブジェクトをあらわす新規オブジェクトリファレンスを作成します。

// IDL
 
interface A { };
 
// C++
 
class A
{
       public:
       static A_ptr _duplicate(A_ptr obj);
};
引数で与えられたオブジェクトリファレンスがnilだった場合_duplicateはnilを返します。

nilオブジェクトリファレンス
interfaceのマッピングによってスタティックメンバ関数_nil()が定義されます。_nil()はそのインタフェースのnilオブジェクトリファレンスを返します。また、CORBA::is_nilはオブジェクトがnilかどうかをチェックします。
 
次の式は常に1を返します。

CORBA::Boolean b = CORBA::is_nil(A::_nil());
オブジェクトリファレンスの比較に==オペレータを使用することはできません。オブジェクトリファレンスがnilかどうかを調べるには上記のようにCORBA::is_nilを使います。

定数(constant)
IDLの定数はC++の定数になります。IDLでグローバルなスコープで定義された定数は、C++のファイルスコープで定義される定数になり、インタフェース中で定義された定数は、C++のクラス中で定義されるスタティックな定数にマッピングされます。
次に例を示します。

// IDL
const string name = "testing";
 
interface A
{
       const float pi = 3.14159;
};
 
// C++
static const char *const name = "testing";
 
class A
{
       public:
       static const Float pi;
};

基本データ型
IDLの基本データ型はC++の型に次のように対応づけられます。
表1.2.2.12-1
OMG IDL C++
short CORBA::Short
long CORBA::Long
long long CORBA::LongLong
unsigned short CORBA::UShort
unsigned long CORBA::ULong
unsigned long long CORBA::ULongLong
float CORBA::Float
double CORBA::Double
long double CORBA::LongDouble
char CORBA::Char
wchar CORBA::WChar
boolean CORBA::Boolean
octet CORBA::Octet
CORBA::Booleanは1(TRUE)か0(FALSE)の値のみが正当な値として定義されており、それ以外の値が設定された場合の挙動は定義されていません。またプラットフォームによりデータ型のサイズが異なるのを吸収するためにCORBAモジュールにそれぞれの基本型がtypedefされています。

列挙型(enum)
IDLのenumはC++のenumにマッピングされます。
次に例を示します。

// IDL
enum Color { red, green, blue };
 
// C++
enum Color { red, green, blue };

文字列(string, wstring)
IDLのstringはC++のchar*にマッピングされます。さらにCORBA::String_var型が用意されています。この型はchar*を保持し、CORBA::String_varがデストラクトされるときに文字列を自動的に解放するようになっています。
同様にワイド文字列を表すwstringはCORBA::WChar*にマッピングされます。また、CORBA::WString_var型が用意されています。
CORBA::String_var型にchar*が代入されると、char*の所有権はCORBA::String_varが持つことになり、メモリの解放はCORBA::String_varが自動的に行うようになります。
ただし、const char*をCORBA::String_varに代入する場合には、const char*の指す内容が新たにコピーされ、CORBA::String_varに代入されます。
次に例を示します。

char * s1ptr = CORBA::string_dup("test string");
const char *cs2ptr = "const test string";
 
{
       // 所有権の管理をs1varが行う
       CORBA::String_var s1var = sptr;
 
       // コピーが作成されそれの所有権の管理をs2varが行う
       CORBA::String_var s2var = cs2ptr;
}
 
// スコープを出るとsptrの指す内容はfreeされる
文字列のアロケーションを行うにはアプリケーションでは次の関数を使う必要があります。通常のcallocやfreeを使うと予期しない場所で実行時エラーになります。 ワイド文字列も、型が異なるだけで、ふるまいは同じです。
CORBA::string_alloc(CORBA::ULong len);
CORBA::string_dup(const char*);
CORBA::string_free(char*);
 
CORBA::wstring_alloc(CORBA::ULong len);
CORBA::wstring_dup(const CORBA::WChar*);
CORBA::wstring_free(CORBA::WChar*);
CORBA::string_allocは文字列の終了をあらわすヌル文字('\0')を含む文字列を保持できるようにlen+1文字分のメモリをアロケートし、先頭アドレスを返します。アロケートできない場合にはヌル・ポインタを返します。
CORBA::string_dupは引数で与えられた文字列の長さ+1の文字数分の領域をアロケートし、そこに文字列をコピーします。アロケートに成功した場合には先頭のアドレスを返し、失敗した場合にはヌル・ポインタを返します。
CORBA::string_freeはCORBA::string_alloc、CORBA::string_dupでアロケートされた文字列を解放します。
CORBA::string_freeにヌル・ポインタを渡してもかまいません。この場合は何も起こりません。
CORBA::wsting_XXXはワイド文字列に使います。使い方は同じです。


構造型(StructuredTypes)
IDLのstruct, union, sequence(ただしarrayを除く)は、デフォルトコンストラクタ、コピーコンストラクタ、代入オペレータ、デストラクタを持つクラスまたは構造体にマッピングされます。デフォルトコンストラクタはメンバを初期化するときに、オブジェクトリファレンスに関してはnilオブジェクトリファレンスで、文字列に関してはヌル・ポインタで初期化します。コピーコンストラクタはdeep-copyを行います。これを行うのに、オブジェクトリファレンスに関しては_duplicateを呼び、文字列に関しては新たにメモリのアロケートを行います。代入オペレータは以前の値を解放し、新しい値をdeep-copyします。デストラクタはオブジェクトリファレンス、文字列の解放を行います。
IDLの構造型に対してはそれが固定長(fixed length)であるか、可変長(variable length)であるかによって扱いが変わります。構造型が固定長であるとは次のときをいいます。

可変長型のout引数や返り値型に対しては、それぞれの型のポインタが返されますが、このポインタの管理を容易にするための型が定義されています。構造型Tに関してはT_varという型が同時に定義されます。この型はT型あるいはT_ptr型と同様にふるまい、メモリ管理を自動的に行うのに使用されます。T_varのメンバアクセスを行うときには矢印オペレータ(->)を使用します。
// IDL
struct S { string name; float age; };
 
void f(out S p);
 
// C++
S a;
S_var b;
f(b);
a = b; // deep-copy
 
cout << "names" << a.name << ", "
<< b->name << endl;

T_Var型
一般的なT_var型の構造を次に示します。
// C++
class T_var
 
{
       public:
       T_var();
       T_var(T *);
       T_var(const T_var &);
       ~T_var();
       T_var &operator=(T *);
       T_var &operator=(const T_var &);
       T *operator-> const ();
       /* in parameter type */ in() const;
       /* inout parameter type */ inout();
       /* out parameter type */ out();
       /* return type */ _retn();
       // other conversion operators to support
       // parameter passing
};
デフォルトコンストラクタは、T*としてヌル・ポインタを持つT_varを作ります。アプリケーションではT_varに正しいポインタを設定する前に->オペレータを使用したり、T*への変換オペレータを使用してはいけません。
T*をとるコンストラクタは、デストラクトされるときにこのT*の解放を自動的に行うT_varを作成します。このとき、引数にヌル・ポインタを指定してはいけません。
T_varのコピーコンストラクタは引数で渡されたオブジェクトをdeep-copyします。ここで作成されたコピーはT_varがデストラクトされるときや新しい値が代入されたときに破棄されます。
デストラクタはT_varで指されているポインタに対してdeleteを行います。ただし、文字列や配列の場合には、CORBA::string_free、T_freeを行います。
T*をとる代入オペレータは古い値を解放後新しい値の所有権を管理するようになります。
通常の代入オペレータはdeep-copyを行います。
矢印オペレータ(->)はT_varに保持されているT*を返しますが、所有権は保持したままです。T_varをT*あるいはT_varで初期化した後でないとこのオペレータを使用できません。
T_var型をconst T*に対して使用することはできません。const T*をT_varに変換して使用したい場合には次のようにして行うことができます。

// C++
const T *t = ...;
T_var tv = new T(*t);
暗黙の型変換はしばしば問題を起こすので、明示的変換関数が用意されています(in, inout, out関数)。これらの関数はそれぞれ対応する引数渡しのモードの引数に対して用いることができます。
_retn関数はT_var型内部で管理しているポインタを解放せずに返します。オブジェクトの実装コード内でT_varで管理していたデータをリターン値として返すときに使用すると便利です。_retn関数を呼び出すと、内部のポインタはヌルに変更され、新たに値を設定しない限り、矢印オペレータなどは使えなくなります。


struct型
IDLのstructはC++のstructにマッピングされます。C++のstructはコンストラクタ、デストラクタ、代入オペレータを持ちません。IDLで定義されたメンバに対応するC++のメンバがstructのメンバとして定義されます。ただし、stringオブジェクトリファレンスのメンバに対しては、メモリ管理が自動的に行われる特殊なクラスが使用されていますが、プログラマがこれを意識する必要はありません。
// IDL
struct Fixed{ float x, y, z; };
 
// C++
Fixed x1 = {1.2, 2.4, 3.6};
Fixed_var x2 = new Fixed;
x2->y = x1.z;
上の例は固定長のstructの使用方法を示しています。x2がスコープの外に出ると、x2が所有権を管理しているオブジェクト(new Fixedでアロケートされたオブジェクト)は自動的にdeleteを使用して解放されます。
次の例は可変長structの使用例です。

// IDL
interface A;
 
struct Variable {
       string name;
       A obj;
};
 
// C++
Variable *v1 = ....;
Variable_var vv1 = v1;
char *s = ...;
A_ptr o = ...;
vv1.name = s; // 古い値は解放されsの領域がコピーされる
vv1.obj = o; // 古い値は解放されoが_duplicateされて保持される
 

union型
IDLのunionはC++のクラスにマッピングされます。出力されたC++クラスのデフォルトコンストラクタは初期化を行いません。アプリケーションでは使用する前に必ず初期化を行う必要があります。
unionの弁別子(discriminant)アクセスのメンバ関数は_dという名前で定義されています。弁別子を設定するための_dは同じメンバに対して別の弁別子が割り当てられている場合にのみ使用することができます(以下の例の4, 5の場合など)。unionの値をアクセス関数で設定することにより、弁別子は自動的に設定されます。設定されている以外の型で取り出しを行ったときの動作は規定されていません。このようなことを行ってはいけません。
defaultの指定を持たないunionで、可能なすべての値がcaseで指定されていないものに関しては_defaultというメンバ関数が定義されます。default()は、弁別子を適当なデフォルト値に設定します。
アクセス関数で新しい値を挿入すると古い値は自動的に解放され、新しい値のコピーが作成されて挿入されます。
次に例を示します。

// IDL
typedef octet Bytes[64];
struct S { long len; };
 
interface A;
 
union U switch (long) {
       case 1: long x;
       case 2: Bytes y;
       case 3: string z;
       case 4:
       case 5: S w;
       default: A obj;
};
 
// C++
typedef CORBA::Octet Bytes[64];
typedef CORBA::Octet Bytes_slice;
 
class Bytes_forany { ... };
 
struct S { CORBA::Long len; };
 
typedef ... A_ptr;
 
class U
{
       public:
       U();
       U(const U&);
       ~U();
      
       // 古い値が解放され新しい値のコピーが設定される
       U &operator=(const U&);
       void _d(CORBA::Long); // 弁別子を設定する
       CORBA::Long _d() const; // 弁別子を参照する
       void x(CORBA::Long);
       CORBA::Long x() const;
       void y(Bytes);
       Bytes_slice *y() const;
       // 古いメモリ領域を解放。コピーは行わない。
       void z(char*);
       // 古いメモリ領域を解放。コピーを行う。
       void z(const char*);
       // 古いメモリ領域を解放。コピーを行う。
       void z(const CORBA::String_var &);
       const char *z() const;
       void w(const S &); // deep-copy<を行う。
       const S &w() const; // リードオンリーアクセス
       S &w(); // リードライトアクセス
       // 古いオブジェクトをリリース。新しいオブジェクトを _duplicate
       void obj(A_ptr);
       A_ptr obj() const; // _duplicateは行われない
};

sequence型
sequenceは現在の長さと最大長をもつ、配列のようにふるまうC++のクラスとしてマッピングされます。最大長指定ありのsequence(bounded sequence)の最大長は固定で決められ、プログラマが制御することはできません。最大長指定なしのsequence(unbounded sequence)は、コンストラクタで最大長を指定することができます。これによって最初にアロケートされるバッファの大きさを制御することができます。現在の長さは最大長指定あり、なしに関わらずプログラマが制御することができます。
最大長指定なしのsequenceでは、長さを最大長よりも大きなものを指定すると、バッファを再割り当てします。これは新しいバッファを割り当て中身をコピーし、古いバッファを解放することによって行われます。
最大長指定ありのsequenceに対して最大長よりも大きな長さを設定してはいけません。この時の動作は規定されていません。
IDLで定義された最大長指定なしのsequence、最大長指定ありのsequenceに対して、それぞれ次のようなクラスが生成されます。
// IDL
typedef sequence<long> LongSeq;
typedef sequence<LongSeq, 3> LongSeqSeq;
 
// C++
class LongSeq // unbounded sequence
{
       public:
       LongSeq(); // default constructor
       LongSeq(CORBA::ULong max); // maximum constructor
       LongSeq( // T *data constructor
               CORBA::ULong max,
               CORBA::ULong length,
               CORBA::Long *value,
               CORBA::Boolean release = 0
       );
       LongSeq(const LongSeq&);
       ~LongSeq();
       ...
};
 
class LongSeqSeq // bounded sequence
{
       public:
       LongSeqSeq(); // default constructor
       LongSeqSeq( // T *data constructor
               CORBA::ULong length,
               LongSeq *value,
               CORBA::Boolean release = 0
       );
       LongSeqSeq(const LongSeqSeq&);
       ~LongSeqSeq();
       ...
};
最大長指定あり/なしに関わらずデフォルトコンストラクタはsequenceの長さを0に設定します。最大長指定ありのsequenceに対して最大長を変更することはできません。最大長指定なしのsequenceのデフォルトコンストラクタは、最大長を0に設定します。最大長指定ありのsequenceのデフォルトコンストラクタはsequenceの中身のバッファ領域をアロケートします。したがって、releaseフラグは1に設定されます。最大長指定なしのsequenceには最大長を指定できるコンストラクタがあります。このコンストラクタは指定された長さを格納するだけのバッファをアロケートします。このコンストラクタもreleaseフラグを1に設定します。
T* dataコンストラクタは長さとsequenceに挿入する内容を指定することができます。最大長指定なしのsequenceに関しては同時に最大長を指定することもできます。このコンストラクタに関してはメモリの所有権の管理をreleaseフラグで行います。このフラグが0だと、メモリ管理はこのコンストラクタの呼び出し元が行うことを意味します。1を設定するとsequenceのオブジェクトが破棄されるときに内容も同時に破棄されるようになります。
releaseが1に設定されたときには、バッファはallocbuf関数でアロケートされたものでなければなりません。sequenceのクラスは内容ベクタを解放するときにfreebufという関数を使用します。
代入オペレータは、左辺をまず解放し、右辺を左辺にdeep-copyします。
デストラクタはreleaseフラグが1の場合にはそれぞれの要素をデストラクトし、内容ベクタをfreebufで解放します。
最大長指定なしのsequenceに対してはmaximum関数は現在の内容ベクタとしてアロケートされているサイズを返します。これにより、新たなメモリ獲得をせずにどれだけの要素を追加できるかなどの情報を得ることができます。最大長指定ありのsequenceのmaximum関数は常に最大長を返します。
サブスクリプトオペレータ(operator[])は指定された場所にある要素を返します。
operator[]を使用する前に必ずlengthメソッドで長さを指定しておくか、あるいはsequenceをT*dataコンストラクタで構築しておかなければなりません。
release関数はsequenceクラス内部のreleaseフラグの現在値を返します。
get_buffer関数を使用するとsequenceクラス内部で管理されている領域のポインタを直接得ることができます。orphan引数がFALSEあるいはconst版のget_buffer関数を呼び出した場合、領域は引き続きsequenceクラスが管理します。このとき、戻り値のポインタは、そのsequenceクラスオブジェクトに対して、非const版の関数が呼ばれるまで有効です。orphan引数をTRUEで呼び出すと、領域の管理は呼び出し側に移ります。すなわち、get_buffer関数を呼び出した側で、領域の使用後にfreebufで解放する必要があります。
replace関数はsequenceクラスの内部で管理されている領域を置き換えます。すなわち、replace関数は、T* dataコンストラクタと同様の動作を行います。

sequenceの例
IDLで定義された最大長指定なしのsequenceと最大長指定ありのsequenceに対するC++のコードを以下に示します。
// IDL
typedef sequence<T> V1; // 最大長指定なしのsequence
typedef sequence<T, 2> V2; // 最大長指定ありのsequence
 
// C++
class V1 // 最大長指定なしのsequence
{
       public:
       V1();
       V1(CORBA::ULong max);
       V1(CORBA::ULong max, CORBA::ULong length, T *data,
               CORBA::Boolean release = 0);
       V1(const V1&);
       ~V1();
       V1 &operator=(const V1&);
       CORBA::ULong maximum() const;
       void length(CORBA::ULong);
       CORBA::ULong length() const;
       T &operator[](CORBA::ULong index);
       const T &operator[](CORBA::ULong index) const;
       Boolean release() const;
       void replace(ULong max, Ulong length, T* data,
               Boolean release = FALSE);
       T* get_buffer(Boolean orphan = FALSE);
       const T* get_buffer() const;
};
 
class V2 // 最大長指定ありのsequence
{
       public:
       V2();
       V2(CORBA::ULong length, T *data, CORBA::Boolean release = 0);
       V2(const V2&);
       ~V2();
       V2 &operator=(const V2&);
       CORBA::ULong maximum() const;
       void length(CORBA::ULong);
       CORBA::ULong length() const;
       T &operator[](CORBA::ULong index);
       const T &operator[](CORBA::ULong index) const;
       Boolean release() const;
       void replace(ULong length, T* data,
               Boolean release = FALSE);
       T* get_buffer(Boolean orphan = FALSE);
       const T* get_buffer() const;
};

sequenceのメモリ管理
// IDL
typedef sequence<string, 3> StringSeq;
 
// C++
char *static_arr[] = {"one", "two", "three"};
char **dyn_arr = StringSeq::allocbuf(3);
 
dyn_arr[0] = CORBA::string_dup("one");
dyn_arr[1] = CORBA::string_dup("two");
dyn_arr[2] = CORBA::string_dup("three");
 
StringSeq seq1(3, static_arr); // releaseフラグは0
StringSeq seq2(3, dyn_arr, 1);
 
seq1[1] = "2"; // 左辺の解放も文字列のコピーも行われない …(1)
 
char *str = CORBA::string_dup("2");
 
seq2[1] = str; // 左辺を解放してからstrポインタが代入される
               
// コピーは行われない …(2)
releaseフラグが0の場合にはT*コンストラクタに渡されたバッファ領域の所有権の管理はアプリケーションで行わなければなりません。これに対し1が指定された場合には、このメモリ領域の管理はsequenceクラスが行うことになります。1を指定してバッファ領域をsequenceに渡した場合にはアプリケーションはこの領域が存在しつづける期間についていかなる仮定もしてはいけません。sequenceはより大きなバッファ領域が必要となったときなどにこの領域を解放し、新たに大きな領域を割り当ててそこに値をコピーすることが可能だからです。0を指定した場合にはsequenceがバッファを自動的に解放することはありませんが、大きなバッファ領域が必要となったときに新たな領域を割り当てることは起こり得ます。
T* dataコンストラクタを使用して構築された、要素としてstringあるいはオブジェクトリファレンスを持つsequenceのoperator[]は、コンストラクタに渡されたreleaseフラグによって動作が変わります。上記の例で(1)の代入はsequenceのreleaseフラグに0が指定されていますので、要素の代入のときに左辺の古い文字列の解放は行わず、右辺のポインタの値が直接代入されます。これに対してreleaseフラグを1で指定した(2)の代入では、sequenceがメモリ管理の責任を持つため、代入に先立ち左辺の古い文字列を解放し、その後ポインタが代入されます。(1)の例でも(2)の例でもポインタが直接代入されることには注意を要します。
seq2[1]="bad habit";
このような行は動的にアロケートされたメモリでないものがポインタとしてsequenceに代入されますが、sequenceはそのメモリを解放する責任がありますから、sequenceが破棄されるときにバッファのメモリおよび要素のメモリを解放します。この例では"bad habit"という文字列を解放してしまうことになり、予期しない場所でアプリケーションが実行時エラーを起こすことになります。
上記の文字列の例はオブジェクトリファレンスの場合も同様です。
一般的にreleaseフラグは1に設定して使用するとメモリリークを起こしにくいプログラムを作成することができますが、0に設定するときには注意深く行う必要があります。

sequenceバッファの獲得/解放
sequenceに設定するバッファは次のメソッドで獲得/解放を行わなくてはいけません。
//IDL
typedef sequence<T> ...;
 
//C++
static T * allocbuf(CORBA::ULong nelm);
static void freebuf(T*);
sequence<T>をtypedefを使用してIDL定義すると、対応するC++のクラスには上記のようにallocbuf、freebufというスタティックでパブリックなメンバ関数が定義されます。バッファの獲得はallocbufを使用します。allocbufの引数は要素数です。メモリが獲得できなかった場合にはヌル・ポインタを返します。freebufはallocbufで獲得したメモリを解放します。
allocbufで獲得された領域のそれぞれの要素のオブジェクトに対してはデフォルトコンストラクタが呼び出されます。ただし、文字列の要素の場合にはヌル・ポインタが、オブジェクトリファレンスを要素として持つ場合には、要素の型を持つnilオブジェクトリファレンスが設定されます。

配列型
配列はC++の配列にマッピングされます。配列の要素が文字列あるいはオブジェクトリファレンスの場合には、要素に対して代入が行われたときには古い値は自動的に解放されます。
// IDL
typedef float F[10];
typedef string V[10];
typedef string M[1][2][3];
 
void op(out F p1, out V p2, out M p3);
 
// C++
F f1; F_var f2;
V v1; V_var v2;
M m1; M_var m2;
 
op(f2, v2, m2);
 
f1[0] = f2[1];
v1[1] = v2[1]; // 古い値を解放し、新しい値をコピーします
m1[0][1][2] = m2[0][1][2]; // 古い値を解放し、新しい値をコピーします
上記の最後の2行では、代入前に左辺の要素に格納されている値は解放されてから、右辺の値がコピーされます。
配列がout引数および返り値に使用されている場合には、その配列のslice型へのポインタに対応づけられます。IDLコンパイラは配列の定義に対して、slice型のtypedefを出力します。slice型の名前は配列の名前に_sliceをつけたものになります。_slice型は配列の次元から最初の次元を取り除いた配列に対するtypedefとして定義されます。
// IDL
typedef long LongArray[4][5];
 
// C++
typedef CORBA::Long LongArray[4][5];
 
// IDLコンパイラによって出力された_slice型の定義
typedef CORBA::Long LongArray_slice[5];
IDLコンパイラは配列名に_varという名前をつけたクラスも出力します。T_var型はオーバロードされたoperator[]を持っており、T_var型をあたかも通常のC++の配列のように扱うことができます。また、T_var型は_slice型を引数にとるコンストラクタと代入オペレータも持っています。配列のアクセスにT_var型を使用すると、配列領域のメモリ管理を自動的に行ってくれるので、メモリ解放忘れなどが少なくなります。前の例のIDL定義では次のようなクラスが出力されます。
// C++
class LongArray_var
{
       public:
       LongArray_var();
       LongArray_var(LongArray_slice*);
       LongArray_var(const LongArray_var &);
       ~LongArray_var();
       LongArray_var &operator=(LongArray_slice*);
       LongArray_var &operator=(const LongArray_var &);
       LongArray_slice &operator[](CORBA::ULong index);
       const LongArray_slice &operator[](CORBA::ULong index) const;
       ...
};
配列型に対してはCORBA::Any型への挿入や取り出しのために_foranyというサフィックスのついたクラスも定義されます。Array_forany型も格納されている配列に対してArray_var同様にアクセスすることができます。ただしArray_foranyはArray_varと異なり、Array_foranyの破壊時に、配列のメモリ領域が自動的に解放されることはありません。Arra_slice*型を引数にとるArray_foranyのコンストラクタは、nocopyというCORBA::Boolean型の引数をとり、この引数はデフォルトが0となっています。
nocopyフラグを1にするとanyへのArray_sliceの挿入時にコピーをしないように指定できます。
// C++
class Array_forany
{
       public:
       Array_forany(Array_slice*, CORBA::Boolean nocopy = 0);
       ...
};
配列を動的にアロケートするためには、アプリケーションは特別な関数を使用しなければなりません。配列がT型として定義されているとすると、次の関数がIDLコンパイラによって生成されます。
// C++
T_slice *T_alloc();
T_slice *T_dup(const T_slice*);
void T_free(T_slice *);
T_alloc関数は配列を動的にアロケートします。もしアロケートに失敗したときにはヌル・ポインタが返されます。T_dupは動的に同じサイズの新しい配列をアロケートし、要素を新しい配列にコピーします。アロケートに失敗したときにはヌル・ポインタが返されます。T_free関数はT_allocあるいはT_dupでアロケートされた領域を解放します。T_freeにヌル・ポインタを渡しても構いません。この場合は何の動作も行われません。
配列の領域をアロケートするときにはT_allocを使用することに注意してください。T_allocを使用しない場合には、予期しない場所でアプリケーションエラーとなる危険があり、原因の究明が非常に困難になる場合があります。

typedef
typedefは型に対する別名を作ります。オリジナルの型がC++でいくつかの型に対応づけられる場合には、typedefはそれぞれの型に対して対応する別名を作ります。次に例を示します。
// IDL
typedef long T;
interface A1;
typedef A1 A2;
typedef sequence<long> S1;
typedef S1 S2;
 
// C++
typedef CORBA::Long T;
 
//A1の定義
typedef A1 A2;
typedef A1_ptr A2_ptr;
typedef A1_var A2_var;
 
// S1の定義
typedef S1 S2;
typedef S1_var S2_var;
配列などのように複数のC++型に対応づけられるようなIDL型に対しては、typedefによりすべてのC++型および関数に対して、C++のtypedefが生成されます。
以下に例を示します。
// IDL
typedef long array[10];
typedef array another_array;
 
// C++
// 配列自体に対するtypedef
typedef array another_array;
 
// _varに対するtypedef
typedef array_var another_array_var;
 
// _sliceに対するtypedef
typedef array_slice another_array_slice;
 
// _foranyに対するtypedef
typedef array_forany another_array_forany;
 
// 関数に対してもalias名でアクセスできるように
// another_array_alloc()が定義される
another_array_slice *another_array_alloc() {
       return array_alloc();
}
 
another_array_slice*another_array_dup(another_array_slice *a) {
       return array_dup(a);
}
 
void another_array_free(another_array_slice *a) {
       array_free(a);
}

any
IDLのany型はIDLの任意の型を、安全に挿入および取り出しのできる型です。anyにはIDLから生成されたC++を挿入したり、あるいは、コンパイル時には型のわかっていないデータを処理する機能があります。コンパイル時にわからない型がanyに挿入されているというケースは、たとえば、受け取る側にはあらかじめわからない型のデータが挿入されているanyをリクエストあるいはレスポンスとして受け取った場合などに起こります。
anyに対して、その中に格納されている値と、型を識別するtypecodeとの不一致が起こることを避けるためにIDLで定義された個々の型に対して、挿入と取り出しのオーバーロードされた関数が生成されます。生成された関数を使用してanyへ値を挿入することにより、値とtypecodeが一致しないというケースや、実際に挿入されている型と異なった型としてanyから値を取り出すというケースを防ぐことができます。オーバロード関数で区別がつくように、IDLからC++への変換時には、個々のIDLの型は、C++として異なった型に変換されるようになっていますが次のようなケースではC++の型だけでは区別ができません。
C++マッピングでは上記のことが問題なく行えるようなしくみを提供しています。

anyへの値の挿入方法
IDLのT型に対してIDLコンパイラは次のようなオーバーロードされた関数を出力します。
// C++
void operator<<=(CORBA::Any&, T);
このタイプの関数はshort, unsigned short, long, unsigned long, float, double, 列挙型、長さ制限なしの文字列(unbounded string)、オブジェクトリファレンス型に対して用意されます。
コピーすると性能を落とすような型に対しては、次の挿入関数が生成されます。
// C++
void operator<<=(CORBA::Any&, const T&); // コピーが行われる。
void operator<<=(CORBA::Any&, T*); // コピーは行われない。
これらの<<=オペレータを使用してanyに値を挿入することができます。次に例を示します。
// C++
CORBA::Long value = 42;
CORBA::Any a;
a <<= value;
上記の例ではanyにCORBA::Longの値として42が設定されると同時に、CORBA::LongをあらわすTypeCodeがanyに設定されます。
コピーが行われないタイプの<<=が使用されたときには、格納されたオブジェクトの所有権はanyに移ります。つまりanyがデストラクトされると格納されているオブジェクトも自動的に解放されます。anyに格納した後で、元のオブジェクトをプログラムで明示的に解放すると2重解放が起こることになり、アプリケーションは予期しない場所でクラッシュする可能性が出てきますので注意する必要があります。また、挿入した後でオブジェクトにアクセスしてもいけません。
// IDL
struct s {
       string ss;
};
 
// C++
CORBA::Any a;
sp = new s;
a <<= sp; // 所有権がanyに移る。
delete sp; // 行ってはいけない。2重解放が起こる。
char *s = sp->ss; // 行ってはいけない。
                 
// anyに挿入した後はsp経由でアクセスを行ってはいけない。
コピーされるタイプでもコピーされないタイプの関数でも、<<=オペレータはanyに格納されていた古い値は自動的に解放されます。

配列のanyへの挿入
配列のanyへの挿入取り出しはArray_forany型を通して行います。
// IDL
typedef long LongArray[4][5];
 
// C++
typedef CORBA::Long LongArray[4][5];
typedef CORBA::Long LongArray_slice[5];
 
class LongArray_forany { ... };
 
void operator<<=(CORBA::Any &, const
LongArray_forany &);
Array_forany型は常に<<=に対してconstへのリファレンスとして渡されます。anyに値がコピーされるのかされないのかはArray_foranyのコンストラクタで制御されます。コンストラクタのnocopy引数に1を指定したときにはコピーは行われず、メモリ領域の所有権はanyに移ります。0を指定したときにはコピーが行われます。
// IDL
struct S {... };
typedef S SA[5];
 
// C++
struct S { ... };
 
typedef S SA[5];
typedef S SA_slice;
 
class SA_forany { ... };
 
SA s;
 
// ...sを初期化...
 
CORBA::Any a;
 
a <<= s; // 1行目
a <<= SA_forany(s); // 2行目
上記の例で1行目ではコピーされないタイプのoperator<<=(CORBA::Any&,S*)が呼ばれます。これは配列の挿入ではなく、単なる構造体の挿入になり、意図した動作にはなりません。配列を挿入するという意図した動作を行うためには2行目のように一度SA_forany型を作ってからanyに挿入する必要があります。
オブジェクトリファレンスに対しては次のオペレータが定義されます。
// IDL
interface T { ... };
 
// C++
void operator<<=(CORBA::Any&, T_ptr); // コピーされるタイプ
void operator<<=(CORBA::Any&, T_ptr*); // コピーが行われないタイプ
コピーが行われないタイプのオブジェクトリファレンスの挿入においては、所有権がanyに移ります。したがって、挿入後はオブジェクトリファレンスに対してCORBA::releaseを呼び出したり、このオブジェクトリファレンスを使用してオブジェクトの参照を行ってはいけません。これに対してコピーが行われるタイプのオペレータを使用すると、オブジェクトリファレンスに対してCORBA::Object::_duplicateが呼び出されます。

anyからの値の取り出し
IDLのT型に対してanyからの取り出しのための次のようなオペレータが用意されます。
// C++
CORBA::Boolean operator>>=(const CORBA::Any&, T&); // コピーされる
このオペレータは、boolean, char, octet, short, unsigned short, long, unsigned long, float, double, 列挙型, 長さ制限のない文字列型(unbounded string), オブジェクトリファレンスに対して使用されます。これら以外の型に対しては、次のオペレータが用意されます。
// C++
// コピーは起こらない
CORBA::Boolean operator>>=(const CORBA::Any&, T*&);
取り出しのメソッドを使用して次のように値を取り出すことができます。
// C++
CORBA::Long value;
CORBA::Any a;
 
a <<= CORBA::Long(42);
 
if (a >>= value) {
       // 値を使用する。
}
上記の例で、オペレータ>>=が呼び出されると、挿入されている値が実際にCORBA::Long型かどうかチェックされます。CORBA::Long型の場合には値がvalueにコピーされ、返り値として1を返します。anyの中に入っているのがCORBA::Longではなかった場合にはvalueの値は変更されず、オペレータは0を返します。
コピーが起こるタイプの型に対しては取り出しはポインタで行われます。
// IDL
struct MyStruct {
       long lmem;
       short smem;
};
上記のようなstructは次のようにして取り出すことができます。
// C++
CORBA::Any a;
 
// aにMyStructの値を設定
 
MyStruct *struct_ptr;
 
if (a >>= struct_ptr) {
       // 値を使用可能
}
取り出しが成功すると呼び出し元はanyが所有権を持つメモリ領域へのポインタを獲得し、>>=は1を返します。呼び出し元は返されたポインタをdeleteしたりCORBA::releaseしたりしてはなりません。ポインタはanyの管理するポインタを得るだけですので、anyの値を変更するような操作を行った後では、ポインタを使用してアクセスすることをしてはなりません。特にT_var型を使用するときには注意が必要です。T_varは格納されているポインタを自動的に解放するからです。
取り出しに失敗するとヌル・ポインタが設定され、>>=は0を返します。
配列の取り出しは次のように、Array_foranyという補助型を使用して行います。arrayをIDLで定義すると次のような補助型がIDLコンパイラによって生成されます。
// IDL
typedef long A[20];
typedef A B[30][40][50];
 
// C++
typedef CORBA::Long A[20];
typedef CORBA::Long A_slice;
 
class A_forany { ... };
 
typedef A B[30][40][50];
typedef A B_slice[40][50];
 
class B_forany { ... };
 
CORBA::Boolean operator>>=(const CORBA::Any &, A_forany&);
 
CORBA::Boolean operator>>=(const CORBA::Any &, B_forany&);
 
CORBA::Any a;
A_forany arr;
 
// aにはA型の配列が入っているとする
if (a >>= arr) {
       // arrが使用可能
}
>>=オペレータはCORBA::Any_var型にも定義されています。

boolean, octet, char, 長さ制限付きの文字列(Bounded String)の区別
boolean, octet, char, 長さ制限付きの文字列のanyへの挿入取り出しのためにCORBA::Anyには次の補助型が定義されています。
// C++
class CORBA::Any
{
       public:
       // booleanの挿入のための補助型
       struct from_boolean {
                from_boolean(CORBA::Boolean b) : val(b) {}
                CORBA::Boolean val;
       };
      
       // octetの挿入のための補助型
       struct from_octet {
                from_octet(CORBA::Octet o) : val(o) {}
                CORBA::Octet val;
       };
      
       // charの挿入のための補助型
       struct from_char
{
                from_char(CORBA::Char c) : val(c) {}
                CORBA::Char val;
       };
      
       // 文字列の挿入のための補助型
       struct from_string {
                from_string(char* s, CORBA::ULong b, CORBA::Boolean nocopy = 0) :
                val(s), bound(b) {}
                char *val;
                CORBA::ULong bound;
       };
      
       void operator<<=(from_boolean);
       void operator<<=(from_char);
       void operator<<=(from_octet);
       void operator<<=(from_string);
      
       // booleanの取り出しのための補助型
       struct to_boolean {
                to_boolean(CORBA::Boolean &b) : ref(b) {}
                CORBA::Boolean &ref;
       };
      
       //charの取り出しのための補助型
       struct to_char {
                to_char(CORBA::Char &c) : ref(c) {}
                CORBA::Char &ref;
       };
      
       // octetの取り出しのための補助型
       struct to_octet {
                to_octet(CORBA::Octet &o) : ref(o) {}
                CORBA::Octet &ref;
       };
      
       // 文字列の取り出しのための補助型
       struct to_string {
                to_string(char *&s, CORBA::ULong b) : val(s), bound(b) {}
                char *&val;
                CORBA::ULong bound;
       };
      
       // CORBA::Object型の取り出しのための補助型
       struct to_object {
                to_object(CORBA::Object_ptr &obj) : ref(obj) {}
                CORBA::Object_ptr &ref;
       };
      
       CORBA::Boolean operator>>=(to_boolean) const;
       CORBA::Boolean operator>>=(to_char) const;
       CORBA::Boolean operator>>=(to_octet) const;
       CORBA::Boolean operator>>=(to_string) const;
       CORBA::Boolean operator>>=(to_object) const;
};
上記の補助型を使用して実際の挿入取り出しは次のように行うことができます。
// C++
CORBA::Boolean b = 1;
CORBA::Any any;
 
any <<= CORBA::Any::from_boolean(b);
 
// ...
 
if (any >>= CORBA::Any::to_boolean(b)) {
       // ...booleanの取り出し成功
}
 
char* p = "bounded";
 
any <<= CORBA::Any::from_string(p, 8); // 長さ制限付き文字列の挿入
 
// ...
 
if (any >>= CORBA::Any::to_string(p, 8)) {
       // ...文字列取り出し成功
}
取り出しに成功した場合、指している先のオブジェクトの所有権はanyにあります。anyの有効期間と独立に値を使用する場合には明示的にコピーや_duplicateを行う必要があります。
from_stringの第2引数が0のときは長さ制限なしの文字列をあらわします。from_stringのコンストラクタのnocopyで1が指定されていると挿入後anyが文字列の所有権を獲得します。
// C++
char* p = CORBA::string_alloc(8);
 
// ...pを初期化...
 
any <<= CORBA::Any::from_string(p, 8, 1); // anyがpの所有権を管理

その他のanyの操作
anyには前節以外に、型に関してチェックを行わない関数があります。これらを使用するとコンパイル時には知らない型の操作を行うことも可能になりますが、anyの値とその型を示すTypeCodeの一貫性を保つ責任はプログラマが持つことになります。
// C++
CORBA::Any(CORBA::TypeCode_ptr tc, void *value,
CORBA::Boolean release = 0);
このコンストラクタは指定したTypeCodeと値が設定されたanyを作ります。TypeCodeはAny内で_duplicateされます。release引数が1の場合はanyが所有権を管理することになり、anyの消滅時や他の値が挿入されたときなどには、古い値をanyが解放します。anyに値を挿入した後では、その値を操作してはいけません。
// C++
void replace(
      
CORBA::TypeCode_ptr,
       void *value,
       CORBA::Boolean release
= 0
);
CORBA::TypeCode_ptr type() const;
const void *value() const;
replace関数はanyの中に挿入されている値を指定されたもので置き換えます。古いTypeCodeは_releaseされ、必要な場合は古い値は解放されます。
type()はanyに挿入されている型のTypeCodeを返します。返されたTypeCodeは呼び出し元で_releaseする必要があります。
value()関数はanyに格納されている値を取り出します。値が入っていないときにはヌル・ポインタを返します。

anyのコンストラクタ・デストラクタ・代入オペレータ
anyのデフォルトコンストラクタはTypCodeとしてtk_null、値として何も持たないanyを作成します。コピーコンストラクタはTypeCodeに対して_duplicateを行い、値に関してはdeep-copyを行います。代入オペレータでは、まず、古いTypeCodeは_releaseし、値も必要に応じて解放します。次に新しいTypeCodeを_duplicateしたのち値をdeep-copyします。
デストラクタではTypeCodeを_releaseし、必要な場合には値を解放します。

CORBA::Any_varクラス
anyにも構造体のT_varなどと同様のCORBA::Any_varというクラスが用意されています。anyをCORBA::Any_varに格納するとanyの領域はCORBA::Any_varが消滅するときに自動的に削除されるようになります。

例外
インタフェース固有の例外(ユーザ定義例外)を定義することができます。例外を定義するにはexceptionを使います。また、定義された例外を返すにはオペレーションの後ろにraisesを指定します。raisesには複数のユーザ定義例外を指定することができます。
例外には、詳細な情報を得るためにメンバを持たせることができます。
下記に例を示します。
// IDL
module M {
       interface Foo {
                // 例外の理由を表すenum
                enum Reason { reason_A, reason_B, reason_C };
      
                // メンバを持たない例外
                exception UserDefinedException1 {};
               
                // enumとunsigned shortのメンバを持つ例外
                exception UserDefinedException2 {
                          Reason m1;
                          unsigned short m2;
                };
               
                op() raises(UserDefinedException1, UserDefinedException2);
       };
};
IDLで定義された例外はC++のCORBA::UserExceptionを継承するクラスに対応づけられます。このクラスは可変長structで対応づけられるクラスと同様に使用します。
// C++
namespace M {
       class Foo {
                ...
               
                class UserDefinedException1 : CORBA::UserException {
                          ...
               
                };
                ...
               
       };
};
標準例外はCORBA::SystemExceptionから継承されるクラスに対応づけられます。CORBA::SystemExceptionクラスは次のような定義になっています。
// C++
enum CORBA::CompletionStatus {
       CORBA::COMPLETED_YES,
       CORBA::COMPLETED_NO,
       CORBA::COMPLETED_MAYBE
};
 
class CORBA::SystemException : public CORBA::Exception
{
       public:
       CORBA::SystemException();
       CORBA::SystemException(const CORBA::SystemException &);
       CORBA::SystemException(CORBA::ULong minor, CORBA::CompletionStatus status);
       ~CORBA::SystemException();
      
       CORBA::SystemException &operator=(const CORBA::SystemException &);
       CORBA::ULong minor() const;
       void minor(CORBA::ULong);
       void _raise();
       CORBA::CompletionStatus completed() const;
       void completed(CORBA::CompletionStatus);
};
CORBA::SystemExceptionのデフォルトコンストラクタで作成されたオブジェクトはminor()で0を返し、completed()はCORBA::COMPLETED_NOを返します。
_raise()は自分自身をthrowするための関数です。CORBA::Exceptionクラスは下記のとおりに定義されています。
// C++
class Exception
{
       public:
       virtual ~Exception();
       virtual void _raise() = 0;
};
個々の例外では、_raise()を多重定義して自分自身をthrowするようにしています。_raise()の実装は下記のとおりです。
// C++
void IndividualException::_raise()
{
       throw *this;
}
個別のシステム例外はCORBA::SystemExceptionを継承して定義されています。次に例を示します。
// C++
class CORBA::UNKNOWN : public CORBA::SystemException { ...
};
class CORBA::BAD_PARAM : public CORBA::SystemException {
... };
コンパイル時に型が決定していないユーザ例外を受け取った場合にはCORBA::UnknownUserExceptionを返します。exception()というメンバ関数を持っており、これにより返されたanyから実際の例外情報を取り出すことができます。
// C++
class CORBA::UnknownUserException : public CORBA::UserException
{
       public:
       CORBA::Any &exception();
};

例外の調べ方
オペレーション呼び出しの後で、例外が存在したかどうか、またどの例外が発生したかを調べるには次のようにします。
// C++
A_var a = ...;
 
try {
       a->op(..., env);
} catch (CORBA::SystemException& sys) {
       // システム例外
} catch (XX& xx) {
       // XX例外
}
catchした例外は解放してはいけません。

オペレーションとアトリビュート
オペレーションは、オペレーションと同じ名前のC++関数に対応づけられます。read-writeのアトリビュートはC++の二つのオーバーロードされた関数に対応づけられます。一つは値をセットするためのものでもう一つは値を得るためのものです。セットする関数は、アトリビュートと同じ型のin引数を一つとり、値を得る関数は、同じ型を返す引数のない関数になります。
readonlyのアトリビュートは値を得る関数のみが定義され、セットする関数は定義されません。アトリビュート関数に対する引数と返り値の扱いは、通常のオペレーションの引数の扱いのルールと同じです。
onewayオペレーションも通常のオペレーションと同様に対応づけられます。
// IDL
interface A
{
       void f();
       oneway void g();
       attribute long x;
};
 
// C++
A_var a;
CORBA::Environment env;
 
a->f(env);
a->g(env);
CORBA::Long n = a->x(env);
a->x(n + 1, env);
IDLで定義したオペレーション以外に、CORBA::Environment型の引数を最後の引数として必ずとります。例外が起こった場合には、この変数に情報が設定されます。

オペレーションに対する暗黙の引数
IDLのオペレーション定義にContextが使用されている場合には、オペレーションの最後から2番目の引数がCORBA::Contextとなります。最後の引数はCORBA::Environment型の引数です。

引数渡しのモード
本節では、オペレーションの引数として与えるオブジェクトに対して、誰がその領域を取得し、その領域を解放するか、また、オペレーションの型は何型で渡されるのかを示します。
基本型や列挙型などでは、引数渡しの方法は単純で、Pという基本型や列挙型を渡すときにはPという型を直接渡し、オブジェクトリファレンスの場合にはA_ptrというように_ptr型で渡します。
複雑な型は誰がメモリを獲得/解放するかの問題があり、複雑になります。in引数に関しては、呼び出し元がメモリを獲得し、読み出しのみなので単純です。outとinoutに関しては、少し複雑です。
複雑な型に関しては、固定長の型に関しては、T&型となり、可変長の型に対しては、T*&となります。struct等では固定長の場合と可変長の場合で型が違ってくることになり注意が必要ですが、T_varを使用するとどちらの場合でも同じように、 T_var&と考えて渡すことができます。
out, inout引数の場合には、オペレーション呼び出し前に、変数に格納されている領域を解放する必要があるときがあります。
// 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; // 次のオペレーションではspは解放されない
f(sp);
上記の例のようにT_varを使用していれば、呼び出し前に変数に格納されている値を解放する必要はありませんが、そうでない場合には解放する必要がある場合がありますので注意する必要があります。
// IDL
void q(out string s);
 
// C++
char *s;
for (int i = 0; i < 10; i++)
       q(s); // メモリリーク
上記の例ではoutで返されたメモリ領域が必ずリークしています。これを防ぐには二つの方法があります。
// C++
char *s;
 
CORBA::String_var svar;
 
for (int i = 0 ; i < 10; i++) {
       q(s);
       CORBA::string_free(s); // 明示的に解放
      
       // または
       q(svar); // 暗黙的に解放される
}
out引数にchar*が使われるときには呼び出し元でそれを解放しなければなりませんが、CORBA::String_varを使用することによって解放を自動的に行わせることができます。コンパイル時、実行時ともにチェックは行われませんが、呼び出し元では、呼び出しによって返された引数等に対して変更を行うことは禁止されています。変更を行うときには、一旦別の領域にコピーしてから、新しい領域を変更して使用しなければなりません。
可変長データは、書き換える前に明示的に解放する必要があります。たとえば、inoutの文字列型の引数に対して代入する前に、オペレーションの実装内で、古い文字列データを解放する必要があります。同様にinoutのインタフェース型の引数は古い値をCORBA::release()する必要があります。
これらのことを確実にするためにT_var型のローカル変数に代入してしまうことができます。
// IDL
interface A;
 
void f(inout string s, inout A obj);
 
// C++
 
void Aimpl::f(char *&s, A_ptr &obj) {
       CORBA::String_var s_tmp = s;
       s = /* new data */;
       A_var obj_tmp = obj;
       obj = /* new reference */;
}
ポインタあるいはポインタへのリファレンス(T*&)で返される引数に関しては、ヌル・ポインタを設定してはいけません。設定した場合の挙動は未定義です。以下のどの場合においてもヌル・ポインタを設定することは禁止されています。
呼び出し元はout引数の値としてヌル・ポインタを使用することは許されています。
呼び出される側(オペレーションの実装側)は以下のすべてにおいてヌル・ポインタを返してはいけません。
  • outと返り値での可変長struct
  • outと返り値での可変長union
  • outと返り値での文字列
  • outと返り値でのシーケンス
  • outと返り値での可変長配列、および返り値での固定長配列
  • outと返り値でのany
表5.2.2.12-2にそれぞれのIDL型に対する、引数と返り値のC++の型を示します。
表1.2.2.12-2 表1引数と返り値の型
Data Type In Inout Out Return
short CORBA::Short CORBA::Short& CORBA::Short& CORBA::Short
long CORBA::Long CORBA::Long& CORBA::Long& CORBA::Long
long long CORBA::LongLong CORBA::LongLong& CORBA::LongLong& CORBA::LongLong
unsigned short CORBA::UShort CORBA::UShort& CORBA::UShort& CORBA::UShort
unsigned long CORBA::ULong CORBA::ULong& CORBA::ULong& CORBA::ULong
unsigned long long CORBA::ULongLong CORBA::ULongLong& CORBA::ULongLong& CORBA::ULongLong
float CORBA::Float CORBA::Float& CORBA::Float& CORBA::Float
double CORBA::Double CORBA::Double& CORBA::Double& CORBA::Double
long double CORBA::LongDouble CORBA::LongDouble& CORBA::LongDouble& CORBA::LongDouble
boolean CORBA::Boolean CORBA::Boolean& CORBA::Boolean& CORBA::Boolean
char CORBA::Char CORBA::Char& CORBA::Char& CORBA::Char
wchar CORBA::WChar CORBA::WChar& CORBA::WChar& CORBA::WChar
octet CORBA::Octet CORBA::Octet& CORBA::Octet& CORBA::Octet
enum enum enum& enum& enum
object reference ptr objref_ptr objref_ptr& objref_ptr& objref_ptr
struct, fixed const struct& struct& struct& struct
struct, variable const struct& struct& struct*& struct*
union, fixed const union& union& union& union
union, variable const union& union& union*& union*
string const char* char*& char*& char*
wstring const CORBA::WChar* CORBA::WChar*& CORBA::WChar*& CORBA::WChar*
sequence const sequence& sequence& sequence*& sequence*
array, fixed const array array array array slice*
array, variable const array array array slice*& array slice*
any const any& any& any*& any*
fixed const fixed& fixed& fixed& fixed
valuetype valuetype* valuetype*& valuetype*& valuetype*
表1.2.2.12-3 呼び出し元での、メモリ領域の解放に関する決まり
inout out return
short 1 1 1
long 1 1 1
long long 1 1 1
unsigned short 1 1 1
unsigned long 1 1 1
unsigned long long 1 1 1
float 1 1 1
double 1 1 1
long 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, fixed 1 1 1
struct, variable 1 3 3
union, fixed 1 1 1
union, variable 1 3 3
string 4 3 3
wstring 4 3 3
sequence 5 3 3
array, fixed 1 1 6
array, variable 1 6 6
any 5 3 3
fixed 1 1 1
valuetype 7 7 7
表5.2.2.12-3の数字の説明
  1. 呼び出し元が領域を確保します。inout引数に対しては呼び出し元が初期値を提供し、呼び出し先はその値を変えてもかまいません。out引数に関しては呼び出し元が領域を確保しますが、初期化の必要はなく、値は呼び出し先で設定します。関数の戻り値は値渡しで返されます。
  2. 呼び出し元がオブジェクトリファレンスの領域を確保します。inout引数に関しては呼び出し元は初期値を提供します。呼び出し先においてinout引数の値を変更したいときには、入力値に対してCORBA::relaseを行い、返却値のための領域を確保しなおします。inoutで渡した値を使い続けるためには、呼び出し元は最初にリファレンスを_duplicateする必要があります。呼び出し元はoutとreturnのオブジェクトリファレンスをreleaseする必要があります。その他の構造中に埋め込まれているオブジェクトリファレンスに関してはその構造自体が自動的にreleaseを行います。
  3. out引数に関しては、呼び出し元がポインタを用意し、呼び出し先に参照渡しで渡します。呼び出し先はそのポインタに正しい引数の型のインスタンスへのポインタをセットします。returnに対しては呼び出し先は同様にポインタを返します。呼び出し先ではどちらの場合でもヌル・ポインタを返してはいけません。どちらの場合でも、返された領域を解放する責任を持つのは呼び出し元です。ローカルでもリモートでも同じように処理できるように、呼び出し元では、呼び出し元が呼び出し先と同じアドレス空間にあるか別の空間にあるかに関わりなく、返された領域を常に解放する必要があります。要求の完了後、呼び出し元は返された領域の値を変更してはいけません。変更したいときには呼び出し元で、返された値の新しいコピーを作成しその値を変更する必要があります。
  4. inout(ワイド)文字列に関しては、呼び出し元が文字列の領域を用意しそのポインタを渡します。呼び出し先では渡された文字列を解放し新しい領域を指すポインタを代入してもかまいません。つまり、返される文字列の長さは入力として渡される文字列の長さで制限されることはありません。呼び出し先で領域が解放されることを考慮し、呼び出し元では文字列の領域をCORBA::(w)string_alloc()を使って確保する必要があります。呼び出し元では、outで返された文字列をCORBA::(w)string_free()を使用して解放する必要があります。呼び出し先ではinout,out,returnに対してヌル・ポインタを返してはいけません。
  5. inoutのシーケンスとanyに関しては、シーケンス/anyに対する代入や変更のときに、シーケンス/anyが構築されたときのboolean型の引数releaseの設定に依存して、その内部に格納されている領域が解放されます。
  6. out引数に関しては、呼び出し元がarray sliceへのポインタを用意し、そのポインタを参照渡しで呼び出し先に渡します。呼び出し先では配列の正しいインスタンスへのポインタを設定します。返り値に対しては、呼び出し先は同様にポインタを返します。どちらの場合でも、呼び出し先ではヌル・ポインタを返してはいけません。どちらの場合でも呼び出し元では返された領域を解放する責任があります。ローカル/リモートで同様に処理できるようにするため、呼び出し元では、返された領域を常に解放する必要があります。リクエストの完了後、呼び出し元は返された領域を変更してはいけません。変更したい場合にはまずコピーを作成しそのコピーを変更する必要があります。
  7. 呼び出し元がvaluetype型のインスタンスの領域を確保します。inout引数に関しては呼び出し元は初期値を提供します。呼び出し先においてinout引数の値を変更したいときには、入力値に対してremove_refを行い、返却値のための領域を確保しなおします。inoutで渡した値を使い続けるためには、呼び出し元はオペレーションの呼び出し後の最初に_add_refを呼び出す必要があります。呼び出し元はoutとreturnのvaluetype型のインスタンスに対して、_remove_refを呼び出す必要があります。その他の構造中に埋め込まれているvaluetype型のインスタンスに関してはその構造自体が自動的に_remove_refを行います。

CORBA2.2で新たに加わった型

固定小数点数(Fixed Types)
IDLのfixed型は以下のテンプレートで表される抽象データ型にマップされます。
// C++ class template
template<CORBA::UShort d, Short s>
class CORBA::Fixed
{
public:
       // Constructors...
       CORBA::Fixed(int val = 0);
       CORBA::Fixed(CORBA::LongDouble val);
       CORBA::Fixed(const CORBA::Fixed<d,s>& val);
       ~CORBA::Fixed();
       // Conversions...
       operator LongDouble() const;
       // Operators...
       CORBA::Fixed<d,s>& operator=(const CORBA::Fixed<d,s>& val);
       CORBA::Fixed<d,s>& operator++();
       CORBA::Fixed<d,s>& operator++(int);
       CORBA::Fixed<d,s>& operator--();
       CORBA::Fixed<d,s>& operator--(int);
       CORBA::Fixed<d,s>& operator+() const;
       CORBA::Fixed<d,s>& operator-() const;
       int operator!() const;
       // Other member functions
       CORBA::UShort fixed_digits() const;
       CORBA::Short fixed_scale() const;
};
template<CORBA::UShort d, CORBA::Short s>
istream& operator>>(istream& is, CORBA::Fixed<d,s>& val);
template<CORBA::UShort d, CORBA::Short s>
ostream& operator<<(ostream& os, const CORBA::Fixed<d,s>& val);

Fixed T_var型
他の型と同様にFixed型にはT_var型が定義されます。FixedのT_var型のセマンティックスは固定長構造体のT_var型と同様です。

abstract interface型
IDL abstract interfaceは、オブジェクトリファレンス型とバリュー型のどちらも扱うことが可能です。そのため、以下の点が通常のinterfaceと異なります。 IDL abstract interfaceは通常のIDL interfaceと同様に、同じ名前のクラスにマッピングされますが、CORBA::Objectを継承せず、CORBA::AbstractBaseを継承します。また、通常のIDL abstract interfaceは通常のIDL interfaceを継承できません。
// IDL
abstract interface AbsI1 {
       void op1();
};
 
abstract interface AbsI2 : AbsI1 {
       void op2();
};
 
// C++
class AbsI1...
{
public :
       void op1(…)
}
 
class AbsI2 : public virtual AbsI1
{
public :
       void op2(…)
}

バリュー型(valuetype)
バリュー型(IDL valuetype)は、オブジェクトを参照(オブジェクトリファレンス)ではなく、値で渡すことができます。バリュー型は、CORBA呼び出しに渡されると、そのステート(データメンバ)は、構造体のメンバのように値が順に送られ、受信側では、それを元にコピーのバリュー型インスタンスが作成される。そのため、コピーされたバリュー型インスタンスは送信側のバリュー型インスタンスとは独立した実体となります。
バリュー型は、任意のステート(データメンバ)を持つことができ、データメンバのアクセシビリティとして、privateとpublicを指定できます。このアクセシビリティのセマンティクスは、C++言語でのそれと同様です。すべてのバリュー型は、CORBA::ValueBaseバリュー型を暗黙的に継承し、このCORBA::ValueBaseバリュー型は、CORBA::Objectがインタフェースについて果たす役割と同様の役割をバリュー型について果たします。
IDLコンパイラは、IDLで定義したvaluetypeと同名のクラスとstateメンバのアクセス関数を実装をもつクラス(OBV_<valuetype名>クラス)を提供します。valuetypeの実装クラスは、このOBV_<valuetype名>クラスとCORBA::DefaultValueRefCountBaseクラスを継承して作成します。IDLのvaluetype内に定義したoperationとattributeは、このvaluetype が継承しているvaluetypeやサポートしているインタフェースで定義されたのものも含めて、実装する必要があります。
また、IDLで定義したoperationとattribute以外にも、自身のコピーインスタンスを生成する_copy_valueメソッドを実装する必要があります。IDL のvaluetype 定義にcustomを指定した場合、ORBA::CustomMarshalクラスを継承しますので、実装クラス内にmarshalおよびunmarshalメソッドも実装する必要があります。
valuetypeを受信する場合、CORBA::ValueFactoryBaseを継承したValueFactory実装クラスを実装する必要があります。このクラスでは、valuetypeの実装クラスを生成するcreate_for_unmarshalメソッドを実装する必要があります。IDL でfactory 宣言により独自の実装クラス生成メソッドを持つファクトリクラスを定義できます。クラス名は、<valuetype名>_<factory名>で、メソッド名はfactory名と同じです。メソッドの引数は IDL operation のパラメータ(in引数のみ)と同じ方法で展開されます。valuetype の実装者は、このファクトリクラスを継承した実装クラスを作成する必要があります。実装したValueFactoryは、valuetype受信前に、CORBA::ORB::register_value__factory関数を使用して、ORBに登録しておく必要があります。
valuetypeの例
// IDL
valuetype Val {
       public long state1;
       private long state2;
       factory val_init(in long state1,in long state2);
       attribute long my_data;
       void my_ope();
};
 
// C++
 
// IDLコンパイラ生成クラス
 
class Val : public virtual CORBA::ValueBase {
protected:
       Val();
       virtual ~Val();
 
       virtual void state2(CORBA::Long state2) = 0;
       virtual CORBA::Long state2() const = 0;
 
public:
       virtual CORBA::Long lmy_data(CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual void my_data(CORBA::Long local_data, CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual void my_ope(CORBA::Environment& _env = Ob_default_environment()) = 0;
 
       virtual void state1(CORBA::Long state1) = 0;
       virtual CORBA::Long state1() const = 0;
 
       static Val* _downcast(CORBA::ValueBase*, CORBA::Environment& _env = Ob_default_environment());
};
 
class OBV_Val : public virtual Val {
protected:
       OBV_Val();
       OBV_Val(CORBA::Long state1, CORBA::Long state2);
       virtual ~OBV_Val();
 
       virtual void state2(CORBA::Long state2);
       virtual CORBA::Long state2() const;
 
public:
       virtual void state1(CORBA::Long state1);
       virtual CORBA::Long state1();
};
 
// valuetype実装クラス
 
class Val_i : public OBV_Val, public virtual
CORBA::DefaultValueRefCountBase {
private:
       CORBA::Long m_my_data;
protected:
       Val_i() : OBV_Val(), m_my_data(0) {}
       Val_i(CORBA::Long a1, CORBA::Long a2, CORBA::Long a3) : OBV_Val(a1, a2), m_my_data(a3) {}
       virtual ~Val_i() {}
public:
       virtual CORBA::Long my_data(CORBA::Environment&) { return m_my_data; }
       virtual void my_data(CORBA::Long _my_data, CORBA::Environment&) { m_my_date = _my_data; }
       virtual void my_ope(CORBA::Environment&) { ... }
       virtual CORBA::ValueBase_ptr _copy_value(CORBA::Environment&)
       { return new Val_i(state1(), state2(), _my_data); }
...
};
 
 
class Val_init_i : public Val_init {
public:
       Val_init_i() {}
       virtual ~Val_init_i() {}
 
       virtual Val* val_init(CORBA::Long state1, CORBA::Long state2, CORBA::Environment&)
       { return new Val_i(state1, state2, 0); }
       virtual CORBA::ValueBase_ptr create_for_unmarshal(CORBA::Environment&)
       { return new Val_i(); }
...
};
バリュー型には、インスタンス化可能なコンクリートバリュー型とステートを持たず、インスタンス化できないアブストラクトバリュー型があります。バリュー型は、1つのコンクリートバリュー型と複数のアブストラクトバリュー型を継承することができますが、アブストラクトバリュー型はコンクリートバリュー型を継承できません。またアブストラクトバリュー型の場合は、OBV_<valuetype名>クラスは生成されません。
1つの通常の(アブストラクトでない)インタフェースと複数のアブストラクトインタフェースをサポートすることができます。ここでのサポートとは、インタフェースで定義されているオペレーション(アトリビュートも含む)をバリュー型に取り込むことです。
concrete valuetypeの例
//IDL
valuetype Val {
       public long state1;
       private long state2;
       factory val_init();
       factory val_init(in long state1,in long state2);
       void local_method(in long arg);
       attribute long local_data;
};
 
//C++
class Val : public virtual CORBA::ValueBase {
protected:
       Val();
       virtual ~Val();
       virtual void state2(CORBA::Long state2) = 0;
       virtual CORBA::Long state2() const = 0;
public:
       virtual void local_method(CORBA::Long arg, CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual CORBA::Long local_data(CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual void local_data(CORBA::Long local_data, CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual void state1(CORBA::Long state1) = 0;
       virtual CORBA::Long state1() const = 0;
       ...
}
class Val_init : public virtual CORBA::ValueFactoryBase
{
protected:
       Val_init();
 
public:
       virtual ~Val_init();
       virtual Val* val_init(CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual Val* val_init(CORBA::Long state1, CORBA::Long state2, CORBA::Environment& _env = Ob_default_environment()) = 0;
       ...
};
 
 
abstract valuetypeの例
// IDL
abstract valuetype AbsVal {
       void local_method(in long arg);
       attribute long local_data;
};
//C++
class AbsVal : public virtual CORBA::ValueBase {
protected:
       AbsVal();
       virtual ~AbsVal();
public:
       virtual void local_method(CORBA::Long arg, CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual CORBA::Long local_data(CORBA::Environment& _env = Ob_default_environment()) = 0;
       virtual void local_data(CORBA::Long local_data, CORBA::Environment& _env = Ob_default_environment()) = 0;
       ...
}
バリュー型のオペレーションの実装は、CORBA呼び出しの発生しないローカル関数になります。
バリュー型をローカル関数の引数に渡した場合は、値渡しになません(コピーされない)。したがって、ローカルスタブのオペレーションに渡された場合もコピーされません。
バリュー型は、null値を送信することができます。

バリューボックス型
バリューボックス型は、単一のステート(データメンバ)を持つバリュー型ですが、以下の事が通常のバリュー型と異ります。
  • オペレーションを持たない。
  • 他の型から派生しない、および、他の型から継承されない。
  • インタフェースをサポートできない。
  • アブストラクトにできない。
  • ユーザによる実装が不要。(IDLコンパイラが生成)
  • バリューファクトリの実装および登録が不要。

1.2.2.13. 付録. WebOTX Object Broker C++/JavaTM 機能差分

WebOTX Object Broker C++と同 Javaでは機能や使い方が異なる部分があります。Javaで作成されたクライアントからC++で作成されたサーバを呼び出すなど、両者が混在した環境で使うプログラムを作成するときや、C++で書かれたプログラムをJavaに移植するときなどは注意する必要があります。
以下に両者の違いを示します。'-'はCORBA仕様に定義されていないことを表します。
表1.2.2.13-1
項目 Java TM C++
IDLコンパイラ i2j(i2j.exe) i2cc(i2cc.exe)
サーバタイムアウト既定値 なし 30秒
moduleマッピング package namespace
interfaceマッピング interface class
out, inout引数 Holderクラス &または*&(型による)
Helperクラス -
signed/unsignedの区別 なし あり
OADが動作するホストの指定 ORBInitialHost(*) なし
OADのポート番号指定 ORBInitialPort(*) OadPort
名前サーバが動作するホストの指定 NameServiceHost(*) NameServiceHostName
インタフェースリポジトリが動作するホストの指定 InterfaceRepositoryHost(*) InterfaceRepositoryHostName
ログ出力レベルの指定 LogLevel(*) LoggingLevel
環境設定ファイルの指定 PropertyFile(*) 環境変数ORBCONFIG
(*)はJavaで書かれたアプリケーション(もしくはアプレット)に対して設定を行う場合のものです。Java版でも、OADや名前サーバなど、C++で書かれたものはC++の設定に従います。