16
// IDL
module M
{
struct E {
long L;
};};
is mapped into:
// C++
}
and E can be referred outside of M as M::E. Alternatively, a C++ using statement for namespace M can be used so that E can be referred to simply as E:
// C++
// C++
16.1.3 CORBA Module
The mapping relies on some predefined types, classes, and functions that are logically defined in a module named CORBA. The module is automatically accessible from a C++ compilation unit that includes a header file generated from an OMG IDL specification. In the examples presented in this document, CORBA definitions are referenced without explicit qualification for simplicity. In practice, fully scoped names or C++ using statements for the CORBA namespace would be required in the application source. Appendix A contains standard OMG IDL types.
16.2 Mapping for Modules
A shown in Section 16.1.1, "Scoped Names," on page 16-1, a module defines a scope, and as such is mapped to a C++ namespace with the same name:
// IDL
// C++
module M
{
// definitions
};
namespace M
{
// definitions
}Because namespaces were only recently added to the C++ language, few C++ compilers currently support them. Alternative mappings for OMG IDL modules that do not require C++ namespaces are in the Appendix "Alternative Mappings for C++ Dialects."
16.3 Mapping for Interfaces
An interface is mapped to a C++ class that contains public definitions of the types, constants, operations, and exceptions defined in the interface.
A CORBA-C++-compliant program cannot
// IDL
interface A
{
struct S { short field; };
};
// C++
// C++
// C++
// IDL// C++
interface A { };
// C++
The _nil function may not throw any CORBA exceptions.
A compliant program cannot attempt to invoke an operation through a nil object reference, since a valid C++ implementation of a nil object reference is a null pointer.
16.3.6 Interface Mapping Example
The example below shows one possible mapping for an interface. Other mappings are also possible, but they must provide the same semantics and usage as this example.
// IDL
// C++
interface A
{
A op(in A param);
};
class A;
typedef A *A_ptr;
typedef A_ptr ARef;
class A : public virtual Object
{
public:
static A_ptr _duplicate(A_ptr obj);
static A_ptr _narrow(Object_ptr obj);
static A_ptr _nil();
virtual A_ptr op(A_ptr param) = 0;
protected:
A();
virtual ~A();
private:
A(const A&);
void operator=(const A&);
};
{
public:
A_var() : ptr_(A::_nil()) {}
A_var(A_ptr p) : ptr_(p) {}
A_var(const A_var &a) : ptr_(A::_duplicate(A_ptr(a))) {}
~A_var() { free(); }
A_var &operator=(A_ptr p) {
reset(p); return *this;
}
operator const A_ptr&() const { return ptr_; }
operator A_ptr&() { return ptr_; }
A_ptr operator->() const { return ptr_; }
protected:
A_ptr ptr_;
void free() { release(ptr_); }
void reset(A_ptr p) { free(); ptr_ = p; }
private:
// hidden assignment operators for var types to
// fulfill the rules specified in Section 16.3.2
void operator=(const A_var &);
void operator=(const _var &);
};
16.4 Mapping for Constants
OMG IDL constants are mapped directly to a C++ constant definition that may or may not define storage depending on the scope of the declaration. In the example below, a top-level IDL constant maps to a file-scope C++ constant whereas a nested constant maps to a class-scope C++ constant. This inconsistency occurs because C++ file-scope constants may not require storage (or the storage may be replicated in each compilation unit), while class-scope constants always take storage. As a side effect, this difference means that the generated C++ header file might not contain values for constants defined in the OMG IDL file.
// IDL
// C++
const string name = "testing";
interface A
{
const float pi = 3.14159;
};
static const char *const name = "testing";
class A
{
public:
static const Float pi;
};In certain situations, use of a constant in OMG IDL must generate the constant's value instead of the constant's name.3 For example,
// IDL
// C++
interface A
{
const long n = 10;
typedef long V[n];
};
class A
{
public:
static const long n;
typedef long V[10];
}; 16.5 Mapping for Basic Data Types
The basic data types have the mappings shown in Table 23 on page 16-10. Note that the mapping of the OMG IDL boolean type defines only the values 1 (TRUE) and 0 (FALSE); other values produce undefined behavior.
Each OMG IDL basic type is mapped to a typedef in the CORBA module. This is because some types, such as short and long, may have different representations on different platforms, and the CORBA definitions will reflect the appropriate representation. For example, on a 64-bit machine where a long integer is 64 bits, the definition of CORBA::Long would still refer to a 32-bit integer. Requirements for the sizes of basic types are shown in Section 3.8.1, "Basic Types," on page 3-20.
Except for boolean, char, and octet, the mappings for basic types must be distinguishable from each other for the purposes of overloading. That is, one can safely write overloaded C++ functions on Short, UShort, Long, ULong, Float, and Double.
Programmers concerned with portability should use the CORBA types. However, some may feel that using these types with the CORBA qualification impairs readability. If the CORBA module is mapped to a namespace, a C++ using statement may help this problem. On platforms where the C++ data type is guaranteed to be identical to the OMG IDL data type, a compliant implementation therefore may generate the native C++ type.
For the Boolean type, only the values 1 (representing TRUE) and 0 (representing FALSE) are defined; other values produce undefined behavior. Since many existing C++ software packages and libraries already provide their own preprocessor macro definitions of TRUE and FALSE, this mapping does not require that such definitions be provided by a compliant implementation. Requiring definitions for TRUE and FALSE could cause compilation problems for CORBA applications that make use of such packages and libraries. Instead, we recommend that compliant applications simply use the values 1 and 0 directly.4 Alternatively, for those C++ compilers that support the new bool type, the keywords true and false may be used.
16.6 Mapping for Enums
An OMG IDL enum maps directly to the corresponding C++ type definition. The only difference is that the generated C++ type may need an additional constant that is large enough to force the C++ compiler to use exactly 32 bits for values declared to be of the enumerated type.
// IDL
// C++
enum Color { red, green, blue };
enum Color { red, green, blue }; 16.7 Mapping For String Types
As in the C mapping, the OMG IDL string type, whether bounded or unbounded, is mapped to char* in C++. String data is null-terminated. In addition, the CORBA module defines a class String_var that contains a char* value and automatically frees the pointer when a String_var object is deallocated. When a String_var is constructed or assigned from a char*, the char* is consumed and thus the string data may no longer be accessed through it by the caller. Assignment or construction from a const char* or from another String_var causes a copy. The String_var class also provides operations to convert to and from char* values, as well as subscripting operations to access characters within the string. The full definition of the String_var interface is given in "String_var Class" on page C-2.
Because its mapping is char*, the OMG IDL string type is the only non-basic type for which this mapping makes size requirements.
For dynamic allocation of strings, compliant programs must use the following functions from the CORBA namespace:
namespace CORBA {
char *string_alloc(ULong len);
char *string_dup(const char*);
void string_free(char *);
...
}The string_alloc function dynamically allocates a string, or returns a null pointer if it cannot perform the allocation. It allocates len+1 characters so that the resulting string has enough space to hold a trailing NUL character. The string_dup function dynamically allocates enough space to hold a copy of its string argument, including the NUL character, copies its string argument into that memory, and returns a pointer to the new string. If allocation fails, a null pointer is returned. The string_free function deallocates a string that was allocated with string_alloc or string_dup. Passing a null pointer to string_free is acceptable and results in no action being performed. These functions allow ORB implementations to use special memory management mechanisms for strings if necessary, without forcing them to replace global operator new and operator new[].
The string_alloc, string_dup, and string_free functions may not throw CORBA exceptions.
Note that a static array of char in C++ decays to a char*, so care must be taken when assigning one to a String_var, since the String_var will assume the pointer points to data allocated via string_alloc and thus will eventually attempt to string_free it:
// The following is an error, since the char* should point to
// data allocated via string_alloc so it can be consumed
String_var s = "static string"; // error
// The following are OK, since const char* are copied,
// not consume
const char* sp = "static string";
s = sp;
s = (const char*)"static string too"; 16.8 Mapping for Structured Types
The mapping for struct, union, and sequence (but not array) is a C++ struct or class with a default constructor, a copy constructor, an assignment operator, and a destructor. The default constructor initializes object reference members to appropriately-typed nil object references and string members to NULL; all other members are initialized via their default constructors. The copy constructor performs a deep-copy from the existing structure to create a new structure, including calling _duplicate on all object reference members and performing the necessary heap allocations for all string members. The assignment operator first releases all object reference members and frees all string members, and then performs a deep-copy to create a new structure. The destructor releases all object reference memebrs and frees all string members.
The mapping for OMG IDL structured types (structs, unions, arrays, and sequences) can vary slightly depending on whether the data structure is fixed-length or variable-length. A type is variable-length if it is one of the following types:
// IDL
// C++
struct S { string name; float age; };
void f(out S p);
S a;
S_var b;
f(b);
a = b; // deep-copy
cout << "names " << a.name << ", " << b->name << endl;
// C++
The T_var types are also produced for fixed-length structured types for reasons of consistency. These types have the same semantics as T_var types for variable-length types. This allows applications to be coded in terms of T_var types regardless of whether the underlying types are fixed- or variable-length.
Each T_var type must be defined at the same level of nesting as its T type.
T_var types do not work with a pointer to constant T, since they provide no constructor nor operator= taking a const T* parameter. Since C++ does not allow delete to be called on a const T*, the T_var object would normally have to copy the const object; instead, the absence of the const T* constructor and assignment operators will result in a compile-time error if such an initialization or assignment is attempted. This allows the application developer to decide if a copy is really wanted or not. Explicit copying of const T* objects into T_var types can be achieved via the copy constructor for T:
const T *t = ...;
T_var tv = new T(*t); 16.9 Mapping for Struct Types
An OMG IDL struct maps to C++ struct, with each OMG IDL struct member mapped to a corresponding member of the C++ struct. This mapping allows simple field access as well as aggregate initialization of most fixed-length structs. To facilitate such initialization, C++ structs must not have user-defined constructors, assignment operators, or destructors, and each struct member must be of self-managed type. With the exception of strings and object references, the type of a C++ struct member is the normal mapping of the OMG IDL member's type.
For a string or object reference member, the name of the C++ member's type is not specified by the mapping-a compliant program therefore cannot create an object of that type. The behavior6 of the type is the same as the normal mapping (char* for string, A_ptr for an interface A) except the type's copy constructor copies the member's storage and its assignment operator releases the member's old storage.
Assignment between a string or object reference member and a corresponding T_var type (String_var or A_var) always results in copying the data, while assignment with a pointer does not. The one exception to the rule for assignment is when a const char* is assigned to a member, in which case the storage is copied.
When the old storage must not be freed (for example, it is part of the function's activation record), one can access the member directly as a pointer using the _ptr field accessor. This usage is dangerous and generally should be avoided.
// IDL
// C++
struct Fixed{ float x, y, z; };
Fixed x1 = {1.2, 2.4, 3.6};
Fixed_var x2 = new Fixed;
x2->y = x1.z;The example above shows usage of the T and T_var types for a fixed-length struct. When it goes out of scope, x2 will automatically free the heap-allocated Fixed object using delete.
The following examples illustrate mixed usage of T and T_var types for variable-length types, using the following OMG IDL definition:
// IDL
// C++
interface A;
struct Variable { string name; };
Variable str1; // str1.name is initially NULL
Variable_var str2 = new Variable; // str2->name is initially NULL
char *non_const;
const char *const2;
String_var string_var;
const char *const3 = "string 1";
const char *const4 = "string 2";
str1.name = const3; // 1: free old storage, copy
str2->name = const4; // 2: free old storage, copyIn the example above, the name components of variables str1 and str2 both start out as null. On the line marked 1, const3 is assigned to the name component of str1; this results in the previous str1.name being freed, and since const3 points to const data, the contents of const3 being copied. In this case, str1.name started out as null, so no previous data needs to be freed before the copying of const3 takes place. Line 2 is similar to line 1, except that str2 is a T_var type.
Continuing with the example:
non_const = str1.name; // 3: no free, no copy
const2 = str2->name; // 4: no free, no copyOn the line marked 3, str1.name is assigned to non_const. Since non_const is a pointer type (char*), str1.name is not freed, nor are the data it points to copied. After the assignment, str1.name and non_const effectively point to the same storage, with str1.name retaining ownership of that storage. Line 4 is identical to line 3, even though const2 is a pointer to const char; str2->name is neither freed nor copied because const2 is a pointer type.
str1.name = non_const; // 5: free, no copy
str1.name = const2; // 6: free old storage, copyLine 5 involves assignment of a char* to str1.name, which results in the old str1.name being freed and the value of the non_const pointer, but not the data it points to, being copied. In other words, after the assignment str1.name points to the same storage as non_const points to. Line 6 is the same as line 5 except that because const2 is a const char*, the data it points to are copied.
str2->name = str1.name; // 7: free old storage, copy
str1.name = string_var; // 8: free old storage, copy
string_var = str2->name; // 9: free old storage, copyOn line 7, assignment is performed to a member from another member, so the original value is of the left-hand member is freed and the new value is copied. Similarly, lines 8 and 9 involve assignment to or from a String_var, so in both cases the original value of the left-hand side is freed and the new value is copied.
str1.name._ptr = str2.name; // 10: no free, no copyFinally, line 10 uses the _ptr field accessor, so no freeing or copying takes place. Such usage is dangerous and generally should be avoided.
ORB implementations concerned with single-process interoperability with the C mapping may overload operator new() and operator delete() for structs so that dynamic allocation uses the same mechanism as the C language dynamic allocation functions. Whether these operators are overloaded by the implementation or not, compliant programs use new to dynamically allocate structs and delete to free them.
16.10 Mapping for Union Types
Unions map to C++ classes with access functions for the union members and discriminant. The default union constructor performs no application-visible initialization of the union. It does not initialize the discriminator, nor does it initialize any union members to a state useful to an application. (The implementation of the default constructor can do whatever type of initialization it wants to, but such initialization is implementation-dependent. No compliant application can count on a union ever being properly initialized by the default constructor alone.)
It is therefore an error for an application to access the union before setting it, but ORB implementations are not required to detect this error due to the difficulty of doing so. The copy constructor and assignment operator both perform a deep-copy of their parameters, with the assignment operator releasing old storage if necessary. The destructor releases all storage owned by the union.
The union discriminant access functions have the name _d to both be brief and avoid name conflicts with the members. The _d discriminator modifier function can only be used to set the discriminant to a value within the same union member. In addition to the _d accessors, a union with an implicit default member provides a _default() member function that sets the discriminant to a legal default value. A union has an implicit default member if it does not have a default case and not all permissible values of the union discriminant are listed.
Setting the union value through an access function automatically sets the discriminant and may release the storage associated with the previous value. Attempting to get a value through an access function that does not match the current discriminant results in undefined behavior. If an access function for a union member with multiple legal discriminant values is used to set the value of the discriminant, the union implementation is free to set the discriminant to any one of the legal values for that member. The actual discriminant value chosen under these circumstances is implementation dependent.
The following example helps illustrate the mapping for union types:
// IDL
// C++
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;
};
typedef Octet Bytes[64];
typedef Octet Bytes_slice;
class Bytes_forany { ... };
struct S { Long len; };
typedef ... A_ptr;
class U
{
public:
U();
U(const U&);
~U();
U &operator=(const U&);
void _d(Long);
Long _d() const;
void x(Long);
Long x() const;
void y(Bytes);
Bytes_slice *y() const;
void z(char*); // free old storage, no copy
void z(const char*); // free old storage, copy
void z(const String_var &); // free old storage, copy
const char *z() const;
void w(const S &); // deep copy
const S &w() const; // read-only access
S &w(); // read-write access
void obj(A_ptr); // release old objref, duplicate
A_ptr obj() const; // no duplicate
};Accessor and modifier functions for union members provide semantics similar to that of struct data members. Modifier functions perform the equivalent of a deep-copy of their parameters, and their parameters should be passed by value (for small types) or by reference to const (for larger types). Accessors that return a reference to a non-const object can be used for read-write access, but such accessors are only provided for the following types: struct, union, sequence, and any.
For an array union member, the accessor returns a pointer to the array slice, where the slice is an array with all dimensions of the original except the first (array slices are described in detail in Section 16.12). The array slice return type allows for read-write access for array members via regular subscript operators. For members of an anonymous array type, supporting typedefs for the array must be generated directly into the union. For example:
// IDL
union U switch (long) {
default: long array[20][20];
};
// C++
class U
{
public:
// ...
void array(long arg[20][20]);
typedef long _array_slice[20];
_array_slice * array();
// ...
};The name of the supporting array slice typedef is created by prepending an underscore and appending "_slice" to the union member name. In the example above, the array member named "array" results in an array slice typedef called "_array_slice" nested in the union class.
For string union members, the char* modifier results in the freeing of old storage before ownership of the pointer parameter is assumed, while the const char* modifier and the String_var modifier7 both result in the freeing of old storage before the parameter's storage is copied. The accessor for a string member returns a const char* to allow examination but not modification of the string storage.8
For object reference union members, object reference parameters to modifier functions are duplicated after the old object reference is released. An object reference return value from an accessor function is not duplicated because the union retains ownership of the object reference.
The restrictions for using the _d discriminator modifier function are shown by the following examples, based on the definition of the union U shown above:
S s = {10};
U u;
u.w(s); // member w selected
u._d(4); // OK, member w selected
u._d(5); // OK, member w selected
u._d(1); // error, different member selected
A_ptr a = ...;
u.obj(a); // member obj selected
u._d(7); // OK, member obj selected
u._d(1); // error, different member selectedAs shown here, the _d modifier function cannot be used to implicitly switch between different union members. The following shows an example of how the _default() member function is used:
// IDL
// C++
union Z switch(boolean) {
case TRUE: short s;
};
Z z;
z._default(); // implicit default member selected
Boolean disc = z._d(); // disc == FALSE
U u; // union U from previous example
u._default(); // error, no _default() providedFor union Z, calling the _default() member function causes the union's value to be composed solely of the discriminator value of FALSE, since there is no explicit default member. For union U, calling _default() causes a compilation error because U has an expliitly declared default case and thus no _default() member function. A _default() member function is only generated for unions with implicit default members.
ORB implementations concerned with single-process interoperability with the C mapping may overload operator new() and operator delete() for unions so that dynamic allocation uses the same mechanism as the C language dynamic allocation functions. Whether these operators are overloaded by the implementation or not, compliant programs use new to dynamically allocate unions and delete to free them.
16.11 Mapping for Sequence Types
A sequence is mapped to a C++ class that behaves like an array with a current length and a maximum length. For a bounded sequence, the maximum length is implicit in the sequence's type and cannot be explicitly controlled by the programmer. For an unbounded sequence, the initial value of the maximum length can be specified in the sequence constructor to allow control over the size of the initial buffer allocation. The programmer may always explicitly modify the current length of any sequence.
For an unbounded sequence, setting the length to a larger value than the current length may reallocate the sequence data. Reallocation is conceptually equivalent to creating a new sequence of the desired new length, copying the old sequence elements zero through length-1 into the new sequence, and then assigning the old sequence to be the same as the new sequence. Setting the length to a smaller value than the current length does not affect how the storage associated with the sequence is manipulated. Note, however, that the elements orphaned by this reduction are no longer accessible and that their values cannot be recovered by increasing the sequence length to its original value.
For a bounded sequence, attempting to set the current length to a value larger than the maximum length given in the OMG IDL specification produces undefined behavior.
For each different named OMG IDL sequence type, a compliant implementation provides a separate C++ sequence type. For example:
// IDL
// C++
typedef sequence<long> LongSeq;
typedef sequence<LongSeq, 3> LongSeqSeq;
class LongSeq // unbounded sequence
{
public:
LongSeq(); // default constructor
LongSeq(ULong max); // maximum constructor
LongSeq( // T *data constructor
ULong max,
ULong length,
Long *value,
Boolean release = FALSE
);
LongSeq(const LongSeq&);
~LongSeq();
...
};
class LongSeqSeq // bounded sequence
{
public:
LongSeqSeq(); // default constructor
LongSeqSeq( // T *data constructor
ULong length,
LongSeq *value,
Boolean release = FALSE
);
LongSeqSeq(const LongSeqSeq&);
~LongSeqSeq();
...
};For both bounded and unbounded sequences, the default constructor (as shown in the example above) sets the sequence length equal to 0. For bounded sequences, the maximum length is part of the type and cannot be set or modified, while for unbounded sequences, the default constructor also sets the maximum length to 0. The default constructor for a bounded sequence always allocates a contents vector, so it always sets the release flag to TRUE.
Unbounded sequences provide a constructor that allows only the initial value of the maximum length to be set (the "maximum constructor" shown in the example above). This allows applications to control how much buffer space is initially allocated by the sequence. This constructor also sets the length to 0 and the release flag to TRUE.
The "T *data" constructor (as shown in the example above) allows the length and contents of a bounded or unbounded sequence to be set. For unbounded sequences, it also allows the initial value of the maximum length to be set. For this constructor, ownership of the contents vector is determined by the release parameter-FALSE means the caller owns the storage, while TRUE means that the sequence assumes ownership of the storage. If release is TRUE, the contents vector must have been allocated using the sequence allocbuf function, and the sequence will pass it to freebuf when finished with it. The allocbuf and freebuf functions are described on "Additional Memory Management Functions" on page 16-25.
The copy constructor creates a new sequence with the same maximum and length as the given sequence, copies each of its current elements (items zero through length-1), and sets the release flag to TRUE.
The assignment operator deep-copies its parameter, releasing old storage if necessary. It behaves as if the original sequence is destroyed via its destructor and then the source sequence copied using the copy constructor.
If release=TRUE, the destructor destroys each of the current elements (items zero through length-1).
For an unbounded sequence, if a reallocation is necessary due to a change in the length and the sequence was created using the release=TRUE parameter in its constructor, the sequence will deallocate the old storage. If release is FALSE under these circumstances, old storage will not be freed before the reallocation is performed. After reallocation, the release flag is always set to TRUE.
For an unbounded sequence, the maximum() accessor function returns the total amount of buffer space currently available. This allows applications to know how many items they can insert into an unbounded sequence without causing a reallocation to occur. For a bounded sequence, maximum() always returns the bound of the sequence as given in its OMG IDL type declaration.
The overloaded subscript operators (operator[]) return the item at the given index. The non-const version must return something that can serve as an lvalue (i.e., something that allows assignment into the item at the given index), while the const version must allow read-only access to the item at the given index.
The overloaded subscript operators may not be used to access or modify any element beyond the current sequence length. Before either form of operator[] is used on a sequence, the length of the sequence must first be set using the length(ULong) modifier function, unless the sequence was constructed using the T *data constructor.
For strings and object references, operator[] for a sequence must return a type with the same semantics as the types used for string and object reference members of structs and arrays, so that assignment to the string or object reference sequence member via operator=() will release old storage when appropriate. Note that whatever these special return types are, they must honor the setting of the release parameter in the T *data constructor with respect to releasing old storage.
For the T *data sequence constructor, the type of T for strings and object references is char* and T_ptr, respectively. In other words, string buffers are passed as char** and object reference buffers are passed as T_ptr*.
16.11.1 Sequence Example
The example below shows full declarations for both a bounded and an unbounded sequence.
// IDL
// C++
typedef sequence<T> V1; // unbounded sequence
typedef sequence<T, 2> V2; // bounded sequence
class V1 // unbounded sequence
{
public:
V1();
V1(ULong max);
V1(ULong max, ULong length, T *data,
Boolean release = FALSE);
V1(const V1&);
~V1();
V1 &operator=(const V1&);
ULong maximum() const;
void length(ULong);
ULong length() const;
T &operator[](ULong index);
const T &operator[](ULong index) const;
};
class V2 // bounded sequence
{
public:
V2();
V2(ULong length, T *data, Boolean release = FALSE);
V2(const V2&);
~V2();
V2 &operator=(const V2&);
ULong maximum() const;
void length(ULong);
ULong length() const;
T &operator[](ULong index);
const T &operator[](ULong index) const;
}; 16.11.2 Using the "release" Constructor Parameter
Consider the following example:
// IDL
// C++
typedef sequence<string, 3> StringSeq;
char *static_arr[] = {"one", "two", "three"};
char **dyn_arr = StringSeq::allocbuf(3);
dyn_arr[0] = string_dup("one");
dyn_arr[1] = string_dup("two");
dyn_arr[2] = string_dup("three");
StringSeq seq1(3, static_arr);
StringSeq seq2(3, dyn_arr, TRUE);
seq1[1] = "2"; // no free, no copy
char *str = string_dup("2");
seq2[1] = str; // free old storage, no copyIn this example, both seq1 and seq2 are constructed using user-specified data, but only seq2 is told to assume management of the user memory (because of the release=TRUE parameter in its constructor). When assignment occurs into seq1[1], the right-hand side is not copied, nor is anything freed because the sequence does not manage the user memory. When assignment occurs into seq2[1], however, the old user data must be freed before ownership of the right-hand side can be assumed, since seq2 manages the user memory. When seq2 goes out of scope, it will call string_free for each of its elements and freebuf on the buffer given to it in its constructor.
When the release flag is set to TRUE and the sequence element type is either a string or an object reference type, the sequence will individually release each element before releasing the contents buffer. It will release strings using string_free, and it will release object references using the release function from the CORBA namespace.
In general, assignment should never take place into a sequence element via operator[] unless release=TRUE due to the possibility for memory management errors. In particular, a sequence constructed with release=FALSE should never be passed as an inout parameter because the callee has no way to determine the setting of the release flag, and thus must always assume that release is set to TRUE. Code that creates a sequence with release=FALSE and then knowingly and correctly manipulates it in that state, as shown with seq1 in the example above, is compliant, but care should always be taken to avoid memory leaks under these circumstances.
As with other out and return values, out and return sequences must not be assigned to by the caller without first copying them. This is more fully explained in Section 16.18, "Argument Passing Considerations," on page 16-41.
When a sequence is constructed with release=TRUE, a compliant application should make no assumptions about the continued lifetime of the data buffer passed to the constructor, since a compliant sequence implementation is free to copy the buffer and immediately free the original pointer.
16.11.3 Additional Memory Management Functions
ORB implementations concerned with single-process interoperability with the C mapping may overload operator new() and operator delete() for sequences so that dynamic allocation uses the same mechanism as the C language dynamic allocation functions. Whether these operators are overloaded by the implementation or not, compliant programs use new to dynamically allocate sequences and delete to free them.
Sequences also provide additional memory management functions for their buffers. For a sequence of type T, the following static member functions are provided in the sequence class public interface:
static T *allocbuf(ULong nelems);
static void freebuf(T *);The allocbuf function allocates a vector of T elements that can be passed to the T *data constructor. The length of the vector is given by the nelems function argument. The allocbuf function initializes each element using its default constructor, except for strings, which are initialized to null pointers, and object references, which are initialized to suitably-typed nil object references. A null pointer is returned if allocbuf for some reason cannot allocate the requested vector. Vectors allocated by allocbuf should be freed using the freebuf function. The freebuf function ensures that the destructor for each element is called before the buffer is destroyed, except for string elements, which are freed using string_free(), and object reference elements, which are freed using release(). The freebuf function will ignore null pointers passed to it. Neither allocbuf nor freebuf may throw CORBA exceptions.
16.11.4 Sequence T_var Type
In addition to the regular operations defined for T_var types, the T_var for a sequence type also supports an overloaded operator[] that forwards requests to the operator[] of the underlying sequence.9 This subscript operator should have the same return type as that of the corresponding operator on the underlying sequence type.
16.12 Mapping For Array Types
Arrays are mapped to the corresponding C++ array definition, which allows the definition of statically-initialized data using the array. If the array element is a string or an object reference, then the mapping uses the same type as for structure members. That is, assignment to an array element will release the storage associated with the old value.
// IDL
// C++
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);
F f1; F_var f2;
V v1; V_var v2;
M m1; M_var m2;
f1[0] = f2[1];
v1[1] = v2[1]; // free old storage, copy
m1[0][1][2] = m2[0][1][2]; // free old storage, copyIn the above example, the last two assignments result in the storage associated with the old value of the left-hand side being automatically released before the value from the right-hand side is copied.
As shown in Table 24 on page 16-44, out and return arrays are handled via pointer to array slice, where a slice is an array with all the dimensions of the original specified except the first one. As a convenience for application declaration of slice types, the mapping also provides a typedef for each array slice type. The name of the slice typedef consists of the name of the array type followed by the suffix "_slice". For example:
// IDL
// C++
typedef long LongArray[4][5];
typedef Long LongArray[4][5];
typedef Long LongArray_slice[5];A T_var type for an array should overload operator[] instead of operator->. The use of array slices also means that a T_var type for an array should have a constructor and assignment operator that each take a pointer to array slice as a parameter, rather than T*. The T_var for the previous example would be:
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[](ULong index);
const LongArray_slice &operator[](Ulong index) const;
// other conversion operators to support
// parameter passing
};Because arrays are mapped into regular C++ arrays, they present special problems for the type-safe any mapping described in Section 16.14. To facilitate their use with the any mapping, a compliant implementation must also provide for each array type a distinct C++ type whose name consists of the array name followed by the suffix _forany. These types must be distinct so as to allow functions to be overloaded on them. Like Array_var types, Array_forany types allow access to the underlying array type, but unlike Array_var, the Array_forany type does not delete the storage of the underlying array upon its own destruction. This is because the Any mapping retains storage ownership, as described in Section 16.14.3.
The interface of the Array_forany type is identical to that of the Array_var type, but it may not be implemented as a typedef to the Array_var type by a compliant implementation since it must be distinguishable from other types for purposes of function overloading. Also, the Array_forany constructor taking an Array_slice* parameter also takes a Boolean nocopy parameter which defaults to FALSE:
class Array_forany
{
public:
Array_forany(Array_slice*, Boolean nocopy = FALSE);
...
};The nocopy flag allows for a non-copying insertion of an Array_slice* into an Any.
Each Array_forany type must be defined at the same level of nesting as its Array type.
For dynamic allocation of arrays, compliant programs must use special functions defined at the same scope as the array type. For array T, the following functions will be available to a compliant program:
T_slice *T_alloc();
T_slice *T_dup(const T_slice*);
void T_free(T_slice *);The T_alloc function dynamically allocates an array, or returns a null pointer if it cannot perform the allocation. The T_dup function dynamically allocates a new array with the same size as its array argument, copies each element of the argument array into the new array, and returns a pointer to the new array. If allocation fails, a null pointer is returned. The T_free function deallocates an array that was allocated with T_alloc or T_dup. Passing a null pointer to T_free is acceptable and results in no action being performed. These functions allow ORB implementations to utilize special memory management mechanisms for array types if necessary, without forcing them to replace global operator new and operator new[].
The T_alloc, T_dup, and T_free functions may not throw CORBA exceptions.
16.13 Mapping For Typedefs
A typedef creates an alias for a type. If the original type maps to several types in C++, then the typedef creates the corresponding alias for each type. The example below illustrates the mapping.
// IDL
// C++
typedef long T;
interface A1;
typedef A1 A2;
typedef sequence<long> S1;
typedef S1 S2;
typedef Long T;
// ...definitions for A1...
typedef A1 A2;
typedef A1_ptr A2_ptr;
typedef A1Ref A2Ref;
typedef A1_var A2_var;
// ...definitions for S1...
typedef S1 S2;
typedef S1_var S2_var;For a typedef of an IDL type that maps to multiple C++ types, such as arrays, the typedef maps to all of the same C++ types and functions that its base type requires. For example:
// IDL
// C++
typedef long array[10];
typedef array another_array;
// ...C++ code for array not shown...
typedef array another_array;
typedef array_var another_array_var;
typedef array_slice another_array_slice;
typedef array_forany another_array_forany;
return array_alloc();
}
inline another_array_slice* another_array_dup(another_array_slice *a) {
return array_dup(a);
}
array_free(a);
} 16.14 Mapping for the Any Type
A C++ mapping for the OMG IDL type any must fulfill two different requirements:
// C++
// C++
// C++
// C++
Type-safe insertion of arrays uses the Array_forany types described in Section 16.12, "Mapping For Array Types," on page 16-26. Compliant implementations must provide a version of operator<<= overloaded for each Array_forany type. For example:
// IDL
// C++
typedef long LongArray[4][5];
typedef Long LongArray[4][5];
typedef Long LongArray_slice[5];
class LongArray_forany { ... };
void operator<<=(Any &, const LongArray_forany &);The Array_forany types are always passed to operator<<= by reference to const.
The nocopy flag in the Array_forany constructor is used to control whether the inserted value is copied (nocopy == FALSE) or consumed (nocopy == TRUE). Because the nocopy flag defaults to FALSE, copying insertion is the default.
// IDL
// C++
struct S {... };
typedef S SA[5];
struct S { ... };
typedef S SA[5];
typedef S SA_slice;
class SA_forany { ... };
SA s;
// ...initialize s...
Any a;
a <<= s; // line 1
a <<= SA_forany(s); // line 2Line 1 results in the invocation of the non-copying operator<<=(Any&, S*) due to the decay of the SA array type into a pointer to its first element, rather than the invocation of the copying SA_forany insertion operator. Line 2 explicitly constructs the SA_forany type and thus results in the desired insertion operator being invoked.
The non-copying version of operator<<= for object references takes the address of teh T_ptr type:
// IDL
// C++
interface T { ... };
void operator<<=(Any&, T_ptr); // copying
void operator<<=(Any&, T_ptr*); // non-copyingThe copying version of operator<<= is also supported on the Any_var type. Note that due to the conversion operators that convert Any_var to Any& for parameter passing, only those operator<<= functions defined as member function of Any need to be explicitly defined for Any_var.
// C++
// C++
// C++
// IDL
struct MyStruct {
long lmem;
short smem;
};
// C++
// IDL
// C++
typedef long A[20];
typedef A B[30][40][50];
typedef Long A[20];
typedef Long A_slice;
class A_forany { ... };
typedef A B[30][40][50];
typedef A B_slice[40][50];
class B_forany { ... };
Boolean operator>>=(const Any &, A_forany&); // for type A
Boolean operator>>=(const Any &, B_forany&); // for type BThe Array_forany types are always passed to operator>>= by reference.
For strings and arrays, applications are responsible for checking the TypeCode of the Any to be sure that they do not overstep the bounds of the array or string object when using the extracted value.
The operator>>= is also supported on the Any_var type. Note that due to the conversion operators that convert Any_var to const Any& for parameter passing, only those operator>>= functions defined as member function of Any need to be explicitly defined for Any_var.
// C++
// C++
// C++
// C++
// C++
// C++
// C++
Environment
parameters to pass exception information, the default release argument can be simulated by providing two overloaded replace functions, one that takes a non-defaulted release parameter and one that takes no release parameter. The second function simply invokes the first with the release parameter set to FALSE.
ORB implementations concerned with single-process interoperability with the C mapping may overload operator new() and operator delete() for Anys so that dynamic allocation uses the same mechanism as the C language dynamic allocation functions. Whether these operators are overloaded by the implementation or not, compliant programs use new to dynamically allocate anys and delete to free them.
16.14.8 The Any Class
The full definition of the Any class can be found in "Any Class" on page C-2.
16.14.9 The Any_var Class
Since Anys are returned via pointer as out and return parameters (see Table 24 on page 16-44), there exists an Any_var class similar to the T_var classes for object references. Any_var obeys the rules for T_var classes described in Section 16.8, calling delete on its Any* when it goes out of scope or is otherwise destroyed. The full interface of the Any_var class is shown in Section C.4, "Any_var Class," on page C-4.
16.15 Mapping for Exception Types
An OMG IDL exception is mapped to a C++ class that derives from the standard UserException class defined in the CORBA module (see Section 16.1.3, "CORBA Module," on page 16-2). The generated class is like a variable-length struct, regardless of whether or not the exception holds any variable-length members. Just as for variable-length structs, each exception member must be self-managing with respect to its storage.
The copy constructor, assignment operator, and destructor automatically copy or free the storage associated with the exception. For convenience, the mapping also defines a constructor with one parameter for each exception member-this constructor initializes the exception members to the given values. For exception types that have a string member, this constructor should take a const char* parameter, since the constructor must copy the string argument. Similarly, constructors for exception types that have an object reference member must call _duplicate on the corresponding object reference constructor parameter. The default constructor performs no explicit member initialization.
The UserException class is derived from a base Exception class, which is also defined in the CORBA module.
All standard exceptions are derived from a SystemException class, also defined in the CORBA module. Like UserException, SystemException is derived from the base Exception class. The SystemException class interface is shown below.
enum CompletionStatus {
COMPLETED_YES,
COMPLETED_NO,
COMPLETED_MAYBE
};
class SystemException : public Exception
{
public:
SystemException();
SystemException(const SystemException &);
SystemException(ULong minor, CompletionStatus status);
~SystemException();
SystemException &operator=(const SystemException &);
ULong minor() const;
void minor(ULong);
CompletionStatus completed() const;
void completed(CompletionStatus);
};The default constructor for SystemException causes minor() to return 0 and completed() to return COMPLETED_NO.
Each specific system exception (described in Section 14.1.6, "Exceptions," on page 14-3) is derived from SystemException:
// C++
class UNKNOWN : public SystemException { ... };
class BAD_PARAM : public SystemException { ... };
// etc.All specific system exceptions are defined within the CORBA module.
This exception hierarchy allows any exception to be caught by simply catching the Exception type:
try {
...
} catch (const Exception &exc) {
...
}Alternatively, all user exceptions can be caught by catching the UserException type, and all system exceptions can be caught by catching the SystemException type:
try {
...
} catch (const UserException &ue) {
...
} catch (const SystemException &se) {
...
}Naturally, more specific types can also appear in catch clauses.
Exceptions are normally thrown by value and caught by reference. This approach lets the exception destructor release storage automatically.
C++ compilers that support official C++ Run Time Type Information (RTTI) need not support narrowing for the Exception hierarchy. RTTI supports, among other things, determination of the run-time type of a C++ object. In particular, the dynamic_cast<T*> operator11 allows for narrowing from a base pointer to a more derived pointer if the object pointed to really is of the more derived type. This operator is not useful for narrowing object references, since it cannot determine the actual type of remote objects, but it can be used to narrow within the exception hierarchy. Since catch clauses can catch by type, this feature is mainly of use for narrowing exceptions received via Environments from the DII.
For those C++ environments that do not support dynamic_cast<T*>, the exception hierarchy provides a narrowing mechanism. This is described in"Without Run-Time Type Information (RTTI)" on page D-3.
Request invocations made through the DII may result in user-defined exceptions that cannot be fully represented in the calling program because the specific exception type was not known at compile-time. The mapping provides the UnknownUserException so that such exceptions can be represented in the calling process:
class UnknownUserException : public UserException
{
public:
Any &exception();
}; 16.16 Mapping For Operations and Attributes
An operation maps to a C++ function with the same name as the operation. Each read-write attribute maps to a pair of overloaded C++ functions (both with the same name), one to set the attribute's value and one to get the attribute's value. The set function takes an in parameter with the same type as the attribute, while the get function takes no parameters and returns the same type as the attribute. An attribute marked readonly maps to only one C++ function, to get the attribute's value. Parameters and return types for attribute functions obey the same parameter passing rules as for regular operations.
OMG IDL oneway operations are mapped the same as other operations; that is, there is no way to know by looking at the C++ whether an operation is oneway or not.
The mapping does not define whether exceptions specified for an OMG IDL operation are part of the generated operation's type signature or not.
// IDL
// C++
interface A
{
void f();
oneway void g();
attribute long x;
};
A_var a;
a->f();
a->g();
Long n = a->x();
a->x(n + 1);Unlike the C mapping, C++ operations do not require an additional Environment parameter for passing exception information-real C++ exceptions are used for this purpose. See Section 16.15, "Mapping for Exception Types," on page 16-38 and "Without Exception Handling" on page D-1 for more details.
16.17 Implicit Arguments to Operations
If an operation in an OMG IDL specification has a context specification, then a Context_ptr input parameter (see Section 17.8.1, "Context Interface," on page 17-8) follows all operation-specific arguments. In an implementation that does not support real C++ exceptions, an output Environment parameter is the last argument, following all operation-specific arguments, and following the context argument if present. The parameter passing mode for Environment is described in "Without Exception Handling" on page D-1.
16.18 Argument Passing Considerations
The mapping of parameter passing modes attempts to balance the need for both efficiency and simplicity. For primitive types, enumerations, and object references, the modes are straightforward, passing the type P for primitives and enumerations and the type A_ptr for an interface type A.
Aggregate types are complicated by the question of when and how parameter memory is allocated and deallocated. Mapping in parameters is straightforward because the parameter storage is caller-allocated and read-only. The mapping for out and inout parameters is more problematic. For variable-length types, the callee must allocate some if not all of the storage. For fixed-length types, such as a Point type represented as a struct containing three floating point members, caller allocation is preferable (to allow stack allocation).
To accommodate both kinds of allocation, avoid the potential confusion of split allocation, and eliminate confusion with respect to when copying occurs, the mapping is T& for a fixed-length aggregate T and T*& for a variable-length T. This approach has the unfortunate consequence that usage for structs depends on whether the struct is fixed- or variable-length; however, the mapping is consistently T_var& if the caller uses the managed type T_var.
The mapping for out and inout parameters additionally requires support for deallocating any previous variable-length data in the parameter when a T_var is passed. Even though their initial values are not sent to the operation, we include out parameters because the parameter could contain the result from a previous call. There are many ways to implement this support. The mapping does not require a specific implementation, but a compliant implementation must free the inaccessible storage associated with a parameter passed as a T_var managed type. The following examples demonstrate the compliant behavior:
// IDL
// C++
struct S { string name; float age; };
void f(out S p);
S_var s;
f(s);
// use s
f(s); // first result will be freed
S *sp; // need not initialize before passing to out
f(sp);
// use sp
delete sp; // cannot assume next call will free old value
f(sp);Note that implicit deallocation of previous values for out and inout parameters works only with T_var types, not with other types:
// IDL
// C++
void q(out string s);
char *s;
for (int i = 0; i < 10; i++)
q(s); // memory leak!Each call to the q function in the loop results in a memory leak because the caller is not invoking string_free on the out result. There are two ways to fix this, as shown below:
char *s;
String_var svar;
for (int i = 0 ; i < 10; i++) {
q(s);
string_free(s); // explicit deallocation
// OR:
q(svar); // implicit deallocation
}Using a plain char* for the out parameter means that the caller must explicitly deallocate its memory before each reuse of the variable as an out parameter, while using a String_var means that any deallocation is performed implicitly upon each use of the variable as an out parameter.
Variable-length data must be explicitly released before being overwritten. For example, before assigning to an inout string parameter, the implementor of an operation may first delete the old character data. Similarly, an inout interface parameter should be released before being reassigned. One way to ensure that the parameter storage is released is to assign it to a local T_var variable with an automatic release, as in the following example:
// IDL
// C++
interface A;
void f(inout string s, inout A obj);
void Aimpl::f(char *&s, A_ptr &obj) {
String_var s_tmp = s;
s = /* new data */;
A_var obj_tmp = obj;
obj = /* new reference */
}To allow the callee the freedom to allocate a single contiguous area of storage for all the data associated with a parameter, we adopt the policy that the callee-allocated storage is not modifiable by the caller. However, trying to enforce this policy by returning a const type in C++ is problematic, since the caller is required to release the storage, and calling delete on a const object is an error12. A compliant mapping therefore is not required to detect this error.
Table 24 on page 16-44 displays the mapping for the basic OMG IDL parameter passing modes and return type according to the type being passed or returned, while Table 25 on page 16-45 displays the same information for T_var types. Table 25 is merely for informational purposes; it is expected that operation signatures will be written in terms of the parameter passing modes shown in Table 24, and that T_var types will support the necessary conversion operators to allow them to be passed directly.
In Table 24, fixed-length arrays are the only case where the type of an out parameter differs from a return value, which is necessary because C++ does not allow a function to return an array. The mapping returns a pointer to a slice of the array, where a slice is an array with all the dimensions of the original specified except the first one.
1
Including pseudo-object references. 2 A slice is an array with all the dimensions of the original except the first one.
|
1
Including pseudo-object references.
|
Type
|
Inout Param
|
Out Param
|
Return Result
|
---|---|---|---|
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
|
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
|
sequence
|
5
|
3
|
3
|
array, fixed
|
1
|
1
|
6
|
array, variable
|
1
|
6
|
6
|
any
|
5
|
3
|
3
|
1
As listed in Table 26 on page 16-45
|
2 This can be achieved by deriving all T_var types for object references from a base _var class, then making the assignment operator for _var private within each T_var type.
3 A recent change made to the C++ language by the ANSI/ISO C++ standardization committees allows static integer constants to be initialized within the class declaration, so for some C++ compilers, the code generation issues described here may not be a problem.
4 Examples and descriptions in this document still use TRUE and FALSE for purposes of clarity.
5 Transmissible pseudo-objects are listed as "general arguments" in Section TBL. 14, "Pseudo-objects," on page A-2.
6
Those implementations concerned with data layout compatibility with the C mapping in this manual will also want to ensure that the sizes of these members match those of their C mapping counterparts.
pubs@omg.org Copyright © 1995, Object Management Group. All rights reserved.