This chapter discusses the mappings from OMG IDL statements to C++.
Note: | Some of the information in this chapter is taken from the Common Object Request Broker: C++ Language Mapping Specification, June 1999, published by the Object Management Group (OMG). Used with permission of the OMG. |
OMG IDL-to-C++ mappings are described for the following:
In addition, the following topics are discussed:
Each OMG IDL data type is mapped to a C++ data type or class.
The basic data types in OMG IDL statements are mapped to C++ typedefs in the CORBA module, as shown in Table 13-1.
Note: | 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. |
Object, pseudo-object, and user-defined types are mapped as shown in Table 13-2.
The mapping for strings and UDTs is described in more detail in the following sections.
A string in OMG IDL is mapped to char *
in C++. Both bounded and unbounded strings are mapped to char *
. CORBA strings in C++ are NULL-terminated and can be used wherever a char * type is used.
If a string is contained within another user-defined type, such as a struct
, it is mapped to a CORBA::String_var
type. This ensures that each member in the struct manages its own memory.
Strings must be allocated and deallocated using the following member functions in the CORBA class:
Note: | The string_alloc function allocates len+1 characters so that the resulting string has enough space to hold a trailing NULL character. |
OMG IDL defines a wchar
data type that encodes wide characters from any character set. As with character data, an implementation is free to use any code set internally for encoding wide characters, though, again, conversion to another form may be required for transmission. The size of wchar
is implementation-dependent.
The syntax for defining a wchar
is:
<wide_char_type> ::= “wchar”
wchar_t wmixed[256];
Note: | The wchar and wstring data types enable users to interact with computers in their native written language. Some languages, such as Japanese and Chinese, have thousands of unique characters. These character sets do not fit within a byte. A number of schemes have been used to support multi-byte character sets, but they have proved to be unwieldy to use. Wide characters and wide strings make it easier to interact with this kind of complexity. |
The wstring
data type represents a sequence of wchar
, except the wide character NULL. The type wstring
is similar to that of type string, except that its element type is wchar
instead of char
. The actual length of a wstring
is set at run time and, if the bounded form is used, must be less than or equal to the bound.
The syntax for defining a wstring
is:
<wide_string_type> ::= “wstring” “<” <positive_int_const> “>”
| “wstring
A code example for wstring
is:
CORBA::WString_var v_upper = CORBA::wstring_dup(wmixed);
wstring
types are built in types just like unsigned long, char, string, double, etc. They can be used directly as parameters, typedef'd, used to construct structs, sequences, unions, arrays, and so forth.
Note: | The wchar and wstring data types enable users to interact with computers in their native written language. Some languages, such as Japanese and Chinese, have thousands of unique characters. These character sets do not fit within a byte. A number of schemes have been used to support multi-byte character sets, but they have proved to be unwieldy to use. Wide characters and wide strings make it easier to interact with this kind of complexity. |
A constant in OMG IDL is mapped to a C++ const
definition. For example, consider the following OMG IDL definition:
// OMG IDL
const string CompanyName = “BEA Systems Incorporated”;
module INVENT
{
const string Name = “Inventory Modules”;
interface Order
{
const long MAX_ORDER_NUM = 10000;
};
};
This definition maps to C++ as follows:
// C++
const char *const
CompanyName = “BEA Systems Incorporated”;
. . .
class INVENT
{
static const char *const Name;
. . .
class Order : public virtual CORBA::Object
{
static const CORBA::Long MAX_ORDER_NUM;
. . .
};
};
Top-level constants are initialized in the generated .h
include file, but module and interface constants are initialized in the generated client stub modules.
The following is an example of a valid reference to the MAX_ORDER_NUM
constant, as defined in the previous example:
CORBA::Long accnt_id = INVENT::Order::MAX_ORDER_NUM;
An enum in OMG IDL is mapped to an enum in C++. For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
enum Reply {ACCEPT, REFUSE};
}
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
enum Reply {ACCEPT, REFUSE};
};
The following is an example of a valid reference to the enum defined in the previous example. You refer to enum as follows:
INVENT::Reply accept_reply;
accept_reply = INVENT::ACCEPT;
A struct in OMG IDL is mapped to a C++ struct.
The generated code for a struct depends upon whether it is fixed-length or variable-length. For more information about fixed-length versus variable-length types, see the section Fixed-length Versus Variable-length User-defined Types.
A variable-length struct contains an additional assignment operator member function to handle assignments between two variable-length structs.
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
// Fixed-length
struct Date
{
long year;
long month;
long day;
};
// Variable-length
struct Address
{
string aptNum;
string streetName;
string city;
string state;
string zipCode;
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
struct Date
{
CORBA::Long year;
CORBA::Long month;
CORBA::Long day;
};
struct Address
{
CORBA::String_var aptNum;
CORBA::String_var streetName;
CORBA::String_var city;
CORBA::String_var state;
CORBA::String_var zipCode;
Address &operator=(const Address &_obj);
};
};
Members of a struct are mapped to the appropriate C++ data type. For basic data types (long, short, and so on), see Table 13-1. For object references, pseudo-object references, and strings, the member is mapped to the appropriate var class:
All other data types are mapped as shown in Table 13-2.
No constructor for a generated struct exists, so none of the members are initialized. Fixed-length structs can be initialized using aggregate initialization. For example:
INVENT::Date a_date = { 1995, 10, 12 };
Variable-length members map to self-managing types; these types have constructors that initialize the member.
A var class is generated for structs. For more information, see the section Using var Classes.
An out class is generated for structs. For more information, see the section Using out Classes.
A union in OMG IDL is mapped to a C++ class. The C++ class contains the following:
For example, consider the following OMG IDL definition:
// OMG IDL
union OrderItem switch (long)
{
case 1: itemStruct itemInfo;
case 2: orderStruct orderInfo;
default: ID idInfo;
};
This definition maps to C++ as follows:
// C++
class OrderItem
{
public:
OrderItem();
OrderItem(const OrderItem &);
~OrderItem();
OrderItem &operator=(const OrderItem&);
void _d (CORBA::Long);
CORBA::Long _d () const;
void itemInfo (const itemStruct &);
const itemStruct & itemInfo () const;
itemStruct & itemInfo ();
void orderInfo (const orderStruct &);
const orderStruct & orderInfo () const;
orderStruct & orderInfo ();
void idInfo (ID);
ID idInfo () const;
. . .
};
The default union constructor does not set a default discriminator value for the union; therefore, you cannot call any union accessor member function until you have set the value of the union. The discriminator is an attribute that is mapped through the _d
member function.
For each member in the union, accessor and modifier member functions are generated.
In the following code, taken from the previous example, two member functions are generated for the ID member function:
void idInfo (ID);
ID idInfo () const;
In this example, the first function (the modifier) sets the discriminator to the default value and sets the value of the union to the specified ID value. The second function, the accessor, returns the value of the union.
Depending upon the data type of the union member, additional modifier functions are generated. The member functions generated for each data type are as follows:
basictype
:void basictype (
TYPE
); // modifier
TYPE
basictype () const; // accessor
objtype
, member functions are generated as follows:void objtype (
TYPE); // modifier
TYPE
objtype () const; // accessor
enumtype
, member functions are generated as follows:void enumtype (
TYPE
); // modifier
TYPE
enumtype () const; // accessor
void stringInfo (char *); // modifier 1
void stringInfo (const char *); // modifier 2
void stringInfo (const CORBA::String_var &); // modifier 3
const char * stringInfo () const; // accessor
char *
parameter passed to it and the union is responsible for calling the CORBA::string_free
member function on this string when the union value changes or when the union is destroyed.type
, as follows:void reftype (
TYPE
&); // modifier
const
TYPE
& reftype () const; // accessor
TYPE
& reftype (); // accessor
type
parameter; instead, the function makes a copy of the data type.void arraytype (
TYPE
); // modifier
TYPE
_slice * arraytype () const; // accessor
type
parameter; instead, the function makes a copy of the array.A var class is generated for a union. For more information, see the section Using var Classes .
An out class is generated for a union. For more information, see the section Using out Classes.
In addition to the accessor and modifiers, the following member functions are generated for an OMG IDL union of type TYPE with switch (long) discriminator:
TYPE
();
TYPE
( const
TYPE
& From);
From
argument specifies the union to be copied.
~
TYPE
();
TYPE
&operator=(const
TYPE
& From);
From
argument specifies the union to be copied.
void _d (CORBA::Long Discrim);
Discrim
argument specifies the new discriminant. The data type of the argument is determined by the OMG IDL data type specified in the switch statement of the union. For each OMG IDL data type, see Table 13-1 for the C++ data type.
union U switch(long) {
case 1:
case 2:
short s;
case 3:
int it;
};
short st;
U u;
u.s(1296); // member "s" selected
st = u.s(); // st == 1296
u._d(2); // OK: member "s" still selected
st = u.s(); // st == 1296
u._d(3); // BAD_PARAM: selecting a different member
_d()
modifier is invoked on a new instance of a union, Tuxedo C++ relaxes the "implicit switching" restriction. In this case, no exception is thrown, and the union is not affected.
U u2;
u2._d(1); // no exception, union is unchanged
st = u2.s(); // error! accessing an uninitialized union
u2.it(1296); // OK: member "it" now selected
CORBA::Long _d () const;
A sequence in OMG IDL is mapped to a C++ class. The C++ class contains the following:
You must set the length before accessing any elements.
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
. . .
typedef sequence<LogItem> LogList;
}
This definition maps to C++ as follows:
// C++
class LogList
{
public:
// Default constructor
LogList();
// Maximum constructor
LogList(CORBA::ULong _max);
// TYPE * data constructor
LogList
(
CORBA::ULong _max,
CORBA::ULong _length,
LogItem *_value,
CORBA::Boolean _relse = CORBA_FALSE
);
// Copy constructor
LogList(const LogList&);
// Destructor
~LogList();
LogList &operator=(const LogList&);
CORBA::ULong maximum() const;
void length(CORBA::ULong);
CORBA::ULong length() const;
LogItem &operator[](CORBA::ULong _index);
const LogItem &operator[](CORBA::ULong _index) const;
static LogItem *allocbuf(CORBA::ULong _nelems);
static void freebuf(LogItem *);
};
};
The operator[]
functions are used to access or modify the sequence element. These operators return a reference to the sequence element. The OMG IDL sequence base type is mapped to the appropriate C++ data type.
For basic data types, see Table 13-1. For object references, TypeCode references, and strings, the base type is mapped to a generated _ForSeq_var
class. The _ForSeq_var
class provides the capability to update a string or an object that is stored within the sequence. This generated class has the same member functions and signatures as the corresponding var class. However, this _ForSeq_var
class honors the setting of the release parameter in the sequence constructor. The distinction is that the _ForSeq_var
class lets users specify the Release
flag, thereby allowing users to control the freeing of memory.
All other data types are mapped as shown in Table 13-2.
A var class is generated for a sequence. For more information, see the section Using var Classes.
An out class is generated for a sequence. For more information, see the section Using out Classes.
For a given OMG IDL sequence SEQ with base type TYPE, the member functions for the generated sequence class are described as follows:
SEQ ();
SEQ (CORBA::ULong Max);
Max
argument specifies the maximum length of the sequence.
SEQ (CORBA::ULong Max, CORBA::ULong Length,
TYPE
* Value,
CORBA::Boolean Release);
Release
flag determines whether elements are released when the sequence is destroyed. Explanations of the arguments are as follows:
CORBA_TRUE
, the sequence assumes ownership of the buffer pointed to by the Value
argument. If the Release
flag is CORBA_ TRUE
, this buffer must be allocated using the allocbuf
member function, because it will be freed using the freebuf
member function when the sequence is destroyed.
SEQ(const S& From);
From
argument specifies the sequence to be copied.
~SEQ();
Release
flag, may free the sequence elements.
SEQ& operator=(const SEQ& From);
Release
flag in the current sequence is set to CORBA_TRUE
. The From
argument specifies the sequence to be copied.
CORBA::ULong maximum( ) const;
void length(CORBA::ULong Length);
Length
argument specifies the new length of the sequence. If the sequence is unbounded and the new length is greater than the current maximum, the buffer is reallocated and the elements are copied to the new buffer. If the new length is greater than the maximum, the maximum is set to the new length.
CORBA::ULong length() const;
TYPE
& operator[](CORBA::ULong Index);
const
TYPE
& operator[](CORBA::ULong Index) const;
Index
argument specifies the index of the element to return. This index cannot be greater than the current sequence length. The length must have been set either using the TYPE * constructor or the length(CORBA::ULong)
modifier. If TYPE is an object reference, TypeCode reference, or string, the return type will be a ForSeq_var
class.
static
TYPE
* allocbuf(CORBA::ULong NumElems);
TYPE
*
constructor. The NumElems argument specifies the number of elements in the buffer to allocate. If the buffer cannot be allocated, NULL is returned.
TYPE
*
constructor with release set to CORBA_TRUE
, it should be freed using the freebuf
member function.
static void freebuf(
TYPE
* Value);
TYPE
*
sequence buffer allocated by the allocbuf
function. The Value
argument specifies the TYPE
*
buffer allocated by the allocbuf
function. A 0 (zero) pointer is ignored.
An array in OMG IDL is mapped to a C++ array definition. For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
. . .
typedef LogItem LogArray[10];
};
This definition maps to C++ as follows:
// C++
module INVENT
{
. . .
typedef LogItem LogArray[10];
typedef LogItem LogArray_slice;
static LogArray_slice * LogArray_alloc(void);
static void LogArray_free(LogArray_slice *data);
};
A slice of an array is an array with all the dimensions of the original array except the first dimension. The member functions for the array-generated classes use a pointer to a slice to return pointers to an array. A typedef for each slice is generated.
For example, consider the following OMG IDL definition:
// OMG IDL
typedef LogItem LogMultiArray[5][10];
This definition maps to C++ as follows:
// C++
typedef LogItem LogMultiArray[5][10];
typedef LogItem LogMultiArray_slice[10];
If you have a one-dimensional array, an array slice is just a type. For example, if you had a one-dimensional array of long
, an array slice would result in a CORBA::Long
data type.
The type of the OMG IDL array is mapped to the C++ array element type in the same manner as structs. For more information, see the section Member Mapping.
A var class is generated for an array. For more information, see the section Using var Classes.
An out class is generated for an array. For more information, see the section Using out Classes.
For each array, there are two static functions for array allocation and deallocation. For a given OMG IDL type TYPE
, the allocation and deallocation routines are as follows:
static
TYPE
_slice *
TYPE
_alloc(void);
TYPE
array, returning a pointer to the allocated TYPE
array. If the array cannot be dynamically allocated, 0 (zero) is returned.
static void
TYPE
_free(
TYPE
_slice * Value);
TYPE
array. The Value
argument is a pointer to the dynamically allocated TYPE
array to be freed.
An exception in OMG IDL is mapped to a C++ class. The C++ class contains the following:
The generated class is similar to a variable-length structure, but with an additional constructor to simplify initialization, and with the static _narrow
member function to determine the type of UserException.
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
exception NonExist
{
ID BadId;
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
class NonExist : public CORBA::UserException
{
public:
static NonExist * _narrow(CORBA::Exception_ptr);
NonExist (ID _BadId);
NonExist ();
NonExist (const NonExist &);
~NonExist ();
NonExist & operator=(const NonExist &);
void _raise ();
ID BadId;
};
};
Attributes (data members) of the Exception class are public, so you may access them directly.
Members of an exception are mapped in the same manner as structs. For more information, see Member Mapping.
All exception members are public data in the C++ class, and are accessed directly.
A var class is generated for an exception. For more information, see the section Using var Classes.
An out class is generated for an exception. For more information, see the sectionUsing out Classes.
For a given OMG IDL exception TYPE
, the generated member functions are as follows:
static
TYPE
* _narrow(CORBA::Exception_ptr Except);
TYPE
exception class if the exception can be narrowed to a TYPE
exception. If the exception cannot be narrowed, 0 (zero) is returned. The TYPE
pointer is not a pointer to a new class. Instead, it is a typed pointer to the original exception pointer and is valid only as long as the Except parameter is valid.
TYPE
( );
TYPE
(member-parameters);
TYPE
(const
TYPE
& From);
TYPE
exception argument. The From
argument specifies the exception to be copied.
~
TYPE
();
TYPE
& operator=(const
TYPE
& From);
TYPE
exception argument. The From
argument specifies the exception to be copied.
void _raise ();
CORBA pseudo-objects may be implemented either as normal CORBA objects or as serverless objects. In the CORBA specification, the fundamental differences between these strategies are:
References to serverless objects are not necessarily valid across computational contexts; for example, address spaces. Instead, references to serverless objects that are passed as parameters may result in the construction of independent, functionally identical copies of objects used by receivers of these references. To support this, the otherwise hidden representational properties (such as data layout) of serverless objects are made known to the ORB. Specifications for achieving this are not contained in this chapter; making serverless objects known to the ORB is an implementation detail.
This chapter provides a standard mapping algorithm for all pseudo-object types. This avoids the need for piecemeal mappings for each of the nine CORBA pseudo-object types, and accommodates any pseudo-object types that may be proposed in future revisions of CORBA. It also avoids representation dependence in the C mapping, while still allowing implementations that rely on C-compatible representations.
Rather than C-PIDL, this mapping uses an augmented form of full OMG IDL to describe serverless object types. Interfaces for pseudo-object types follow the same rules as normal OMG IDL interfaces, with the following exceptions:
pseudo
.
The pseudo
prefix means that the interface may be implemented in either a normal or serverless fashion. That is, apply either the rules described in the following sections, or the normal mapping rules described in this chapter.
Serverless objects are mapped in the same way as normal interfaces, except for the differences outlined in this section.
Classes representing serverless object types are not subclasses of CORBA::Object
, and are not necessarily subclasses of any other C++ class. Thus, they do not necessarily support, for example, the Object::create_request
operation.
For each class representing a serverless object type T
, overloaded versions of the following functions are provided in the CORBA namespace:
// C++
void release(T_ptr);
Boolean is_nil(T_ptr p);
The mapped C++ classes are not guaranteed to be usefully subclassable by users, although subclasses can be provided by implementations. Implementations are allowed to make assumptions about internal representations and transport formats that may not apply to subclasses.
The member functions of classes representing serverless object types do not necessarily obey the normal memory management rules. This is because some serverless objects, such as CORBA::NVList
, are essentially just containers for several levels of other serverless objects. Requiring callers to explicitly free the values returned from accessor functions for the contained serverless objects would be counter to their intended usage.
All other elements of the mapping are the same. In particular:
T_ptr
, may or may not simply be a typedef of T*
.// C++
static T_ptr _duplicate(T_ptr p);
static T_ptr _nil();
_duplicate
include simply returning the argument or constructing references to a new instance. Individual implementations may provide stronger guarantees about behavior.All serverless object interfaces and declarations that rely on them have direct analogs in the C mapping. The mapped C++ classes can, but need not, be implemented using representations compatible to those chosen for the C mapping. Differences between the pseudo-object specifications for C-PIDL and C++ PIDL are as follows:
release
performs the role of the associated free
and delete
operations in the C mapping, unless otherwise noted.Brief descriptions and listings of each pseudo-interface and its C++ mapping are provided in the following sections. Further details, including definitions of types referenced but not defined below, may be found in the relevant sections of this document.
A typedef in OMG IDL is mapped to a typedef in C++. Depending upon the OMG IDL data type, additional typedefs and member functions may be defined. The generated code for each data type is as follows:
Basic data types map to a simple typedef. For example:
// OMG IDL
typedef long ID;
// C++
typedef CORBA::Long ID;
A string typedef is mapped to a simple typedef. For example:
// OMG IDL
typedef string IDStr;
// C++
typedef char * IDStr;
Object, interfaces, and TypeCode types are mapped to four typedefs. For example:
// OMG IDL
typedef Item Intf;
// C++
typedef Item Intf;
typedef Item_ptr Intf_ptr;
typedef Item_var Intf_var;
typedef Item_ptr & Intf _out;
UDTs are mapped to three typedefs. For example:
// OMG IDL
typedef LogList ListRetType;
// C++
typedef LogList ListRetType;
typedef LogList_var ListRetType_var;
typedef LogList_out & ListRetType_out;
Arrays are mapped to four typedefs and the static member functions to allocate and free memory. For example:
// OMG IDL
typedef LogArray ArrayRetType;
// C++
typedef LogArray ArrayRetType;
typedef LogArray_var ArrayRetType_var;
typedef LogArray_forany ArrayRetType_forany;
typedef LogArray_slice ArrayRetType_slice;
ArrayRetType_slice * ArrayRetType_alloc();
void ArrayRetType_free(ArrayRetType_slice *);
An operation in OMG IDL is mapped to a C++ member function.
The name of the member function is the name of the operation. The operation is defined as a member function in both the interface class and the stub class. The interface class is virtual; the stub class inherits from the virtual class and contains the member function code from the client application stub. When an operation is invoked on the object reference, the code contained in the corresponding stub member function executes.
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
interface Order
{
. . .
ItemList modifyOrder (in ItemList ModifyList);
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
class Order : public virtual CORBA::Object
{
. . .
virtual ItemList * modifyOrder (
const ItemList & ModifyList) = 0;
};
};
class Stub_Order : public Order
{
. . .
ItemList * modifyOrder (
const ItemList & ModifyList);
};
The generated client application stub then contains the following generated code for the stub class:
// ROUTINE NAME: INVENT::Stub_Order::modifyOrder
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for operation
// modifyOrder.
// (Interface : Order)
INVENT::ItemList * INVENT::Stub_Order::modifyOrder (
const INVENT::ItemList & ModifyList)
{
. . .
}
Each of the arguments in an operation is mapped to the corresponding C++ type as described in Table 13-1 and Table 13-2.
The parameter passing modes for arguments in an operation are described in Table 13-7 and Table 13-8.
The signature of an implementation member function is the mapped signature of the OMG IDL operation. Unlike the client side, the server-side mapping requires that the function header include the appropriate exception (throw
) specification. This requirement allows the compiler to detect when an invalid exception is raised, which is necessary in the case of a local C++-to-C++ library call (otherwise, the call would have to go through a wrapper that checks for a valid exception). For example:
// IDL
interface A
{
exception B {};
void f() raises(B);
};
// C++
class MyA : public virtual POA_A
{
public:
void f() throw(A::B, CORBA::SystemException);
...
};
Since all operations and attributes may throw CORBA system exceptions, CORBA::SystemException
must appear in all exception specifications, even when an operation has no raises
clause.
Within a member function, the “this” pointer refers to the implementation object’s data as defined by the class. In addition to accessing the data, a member function may implicitly call another member function defined by the same class. For example:
// IDL
interface A
{
void f();
void g();
};
// C++
class MyA : public virtual POA_A
{
public:
void f() throw(SystemException);
void g() throw(SystemException);
private:
long x_;
};
void
MyA::f() throw(SystemException)
{
this->x_ = 3;
this->g();
}
However, when a servant member function is invoked in this manner, it is being called simply as a C++ member function, not as the implementation of an operation on a CORBA object. In such a context, any information available via the POA_Current
object refers to the CORBA request invocation that performed the C++ member function invocation, not to the member function invocation itself.
In several existing ORB implementations, each skeleton class derives from the corresponding interface class. For example, for interface Mod::A
, the skeleton class POA_Mod::A
is derived from class Mod::A
. These systems, therefore, allow an object reference for a servant to be implicitly obtained via normal C++ derived-to-base conversion rules:
// C++
MyImplOfA my_a; // declare impl of A
A_ptr a = &my_a; // obtain its object reference
// by C++ derived-to-base conversion
Such code can be supported by a conforming ORB implementation, but it is not required, and is thus not portable. The equivalent portable code invokes _this()
on the implementation object to implicitly register it if it has not yet been registered, and to get its object reference:
// C++
MyImplOfA my_a; // declare impl of A
A_ptr a = my_a._this(); // obtain its object reference
Objects registered with POAs use sequences of octet, specifically the PortableServer::POA::ObjectId
type, as object identifiers. However, because C++ programmers often want to use strings as object identifiers, the C++ mapping provides several conversion functions that convert strings to ObjectId
and vice versa:
// C++
namespace PortableServer
{
char* ObjectId_to_string(const ObjectId&);
ObjectId* string_to_ObjectId(const char*);
}
These functions follow the normal C++ mapping rules for parameter passing and memory management.
If conversion of an ObjectId
to a string would result in illegal characters in the string (such as a NULL), the first two functions throw the CORBA::BAD_PARAM
exception.
A module in OMG IDL is mapped to a C++ class. Objects contained in the module are defined within this C++ class. Because interfaces and types are also mapped to classes, nested C++ classes result.
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
interface Order
{
. . .
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
class Order : public virtual CORBA::Object
{
. . .
}; // class Order
}; // class INVENT
Multiple nested modules yield multiple nested classes. Anything inside the module will be in the module class. Anything inside the interface will be in the interface class.
OMG IDL allows modules, interfaces, and types to have the same name. However, when generating files for the C++ language, having the same name is not allowed. This restriction is necessary because the OMG IDL names are generated into nested C++ classes with the same name; this is not supported by C++ compilers.
Note: | The BEA Tuxedo OMG IDL compiler outputs an informational message if you generate C++ code from OMG IDL with an interface or type with the same name as the current module name. If you ignore this informational message and do not use unique names to differentiate the interface or type from the module name, the compiler will signal errors when compiling the generated files. |
An interface in OMG IDL is mapped to a C++ class. This class contains the definitions of the operations, attributes, constants, and user-defined types (UDTs) contained in the OMG IDL interface.
For an interface INTF, the generated interface code contains the following items:
For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
interface Order
{
void cancelOrder ();
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
class Order;
typedef Order * Order_ptr;
class Order : public virtual CORBA::Object
{
. . .
static Order_ptr _duplicate(Order_ptr obj);
static Order_ptr _narrow(CORBA::Object_ptr obj);
static Order_ptr _nil();
virtual void cancelOrder () = 0;
. . .
};
};
The object reference types and static member functions are described in the following sections, as are UDTs, operations, and attributes.
This section describes in detail the generated static member functions: _duplicate, _narrow,
and _nil
for an interface INTF.
static
INTF
_ptr _duplicate (
INTF
_ptr Obj)
CORBA::release
member function. If an error occurs, a reference to the nil INTF object is returned. The argument Obj
specifies the object reference to be duplicated.
static
INTF
_ptr _narrow (CORBA::Object_ptr Obj)
CORBA::Object_ptr
object reference. The Object_ptr
object reference may have been created by a call to the CORBA::ORB::string_to_object
member function or may have been returned as a parameter from an operation.
INTF
_ptr
object reference must correspond to an INTF object or to an object that inherits from the INTF object. The new INTF object reference must be released by calling the CORBA::release member
function. The argument Obj
specifies the object reference to be narrowed to an INTF object reference. The Obj
parameter is not modified by this member function and should be released by the user when it is no longer required. If Obj
cannot be narrowed to an INTF object reference, the INTF nil object reference is returned.
static
INTF
_ptr _nil ( )
INTF interface. The new reference does not have to be released by calling the CORBA::release
member function.
An interface class (INTF) is a virtual class; the CORBA standard does not allow you to:
Instead, you use one of the object reference types,
INTF
_ ptr
or INTF
_var
class.
You can obtain an object reference by using the _narrow
static member function. Operations are invoked on these classes using the arrow operator (->
).
The INTF
_var
class simplifies memory management by automatically releasing the object reference when the INTF
_var
class goes out of scope or is reassigned. Variable types are generated for many of the UDTs and are described in Using var Classes.
A read-only attribute in OMG IDL is mapped to a C++ function that returns the attribute value. A read-write attribute maps to two overloaded C++ functions, one to return the attribute value and one to set the attribute value. The name of the overloaded member function is the name of the attribute.
Attributes are generated in the same way that operations are generated. They are defined in both the virtual and the stub classes. For example, consider the following OMG IDL definition:
// OMG IDL
module INVENT
{
interface Order
{
. . .
attribute itemStruct itemInfo;
};
};
This definition maps to C++ as follows:
// C++
class INVENT
{
. . .
class Item : public virtual CORBA::Object
{
. . .
virtual itemStruct * itemInfo ( ) = 0;
virtual void itemInfo (
const itemStruct & itemInfo) = 0;
};
};
class Stub_Item : public Item
{
. . .
itemStruct * itemInfo ();
void itemInfo (
const itemStruct & itemInfo);
};
The generated client application stub then contains the following generated code for the stub class:
// ROUTINE NAME: INVENT::Stub_Item::itemInfo
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
INVENT::itemStruct * INVENT::Stub_Item::itemInfo ( )
{
. . .
}
//
// ROUTINE NAME: INVENT::Stub_Item::itemInfo
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
void INVENT::Stub_Item::itemInfo (
const INVENT::itemStruct & itemInfo)
{
}
An attribute is equivalent to two operations, one to return the attribute and one to set the attribute. For example, the itemInfo
attribute listed above is equivalent to:
void itemInfo (in itemStruct itemInfo);
itemStruct itemInfo ();
The argument mapping for the attribute is the same as the mapping for an operation argument. The attribute is mapped to the corresponding C++ type as described in Table 13-1 and Table 13-2. The parameter passing modes for arguments in an operation are described in Table 13-7 and Table 13-8.
An any
in OMG IDL is mapped to the CORBA::Any
class. The CORBA::Any
class handles C++ types in a type-safe manner.
To decrease the chances of creating an any
with a mismatched TypeCode and value, the C++ function overloading facility is utilized. Specifically, for each distinct type in an OMG IDL specification, overloaded functions to insert and extract values of that type are provided. Overloaded operators are used for these functions to completely avoid any name space pollution. The nature of these functions, which are described in detail below, is that the appropriate TypeCode is implied by the C++ type of the value being inserted into or extracted from the any
.
Since the type-safe any
interface described below is based upon C++ function overloading, it requires C++ types generated from OMG IDL specifications to be distinct. However, there are special cases in which this requirement is not met:
char*
regardless of whether they are bounded or unbounded, another means of creating or setting an any
with a bounded string value is necessary. This is described in Distinguishing Boolean, Octet, Char, and Bounded Strings.any
when dealing with arrays is described below and in Arrays.
To allow a value to be set in an any
in a type-safe fashion, the following overloaded operator function is provided for each separate OMG IDL type T:
// C++
void operator<<=(Any&, T);
This function signature suffices for the following types, which are usually passed by value:
For values of type T that are too large to be passed by value efficiently, two forms of the insertion function are provided:
// C++
void operator<<=(Any&, const T&); // copying form
void operator<<=(Any&, T*); // non-copying form
Note that the copying form is largely equivalent to the first form shown, as far as the caller is concerned.
These “left-shift-assign” operators are used to insert a typed value into an any
, as follows:
// C++
Long value = 42;
Any a;
a <<= value;
In this case, the version of operator<<=
overloaded for type Long
sets both the value and the TypeCode properly for the Any variable.
Setting a value in an any
using operator<<=
means the following:
operator<<=
, the lifetime of the value in the Any is independent of the lifetime of the value passed to operator<<=
. The implementation of the Any does not store its value as a reference or a pointer to the value passed to operator<<=
.operator<<=
, the inserted T*
is consumed by the Any. The caller may not use the T*
to access the pointed-to data after insertion because the Any assumes ownership of T*, and the Any may immediately copy the pointed-to data and destroy the original. operator<<=
, any previous value held by the Any
is properly deallocated. For example, if the Any(TypeCode_ptr,void*,TRUE)
constructor (described in Handling Untyped Values) were called to create the Any
, the Any
is responsible for deallocating the memory pointed to by the void*
before copying the new value.Copying insertion of a string type causes the following function to be invoked:
// C++
void operator<<=(Any&, const char*);
Since all string types are mapped to char*
, this insertion function assumes that the value being inserted is an unbounded string. Distinguishing Boolean, Octet, Char, and Bounded Strings describes how bounded strings may be correctly inserted into an Any
. Noncopying insertion of both bounded and unbounded strings can be achieved using the Any::from_string
helper type described in Distinguishing Boolean, Octet, Char, and Bounded Strings.
Type-safe insertion of arrays uses the Array_forany
types described in Arrays. The ORB provides a version of operator<<=
overloaded for each Array_forany
type. For example:
// IDL
typedef long LongArray[4][5];
// C++
typedef Long LongArray[4][5];
typedef Long LongArray_slice[5];
class LongArray_forany { ... };
void operator<<=(Any &, const LongArray_forany &);
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.
Because of the type ambiguity between an array of T
and a T*
, it is highly recommended that portable code explicitly use the appropriate Array_forany
type when inserting an array into an Any. For example:
// IDL
struct S {... };
typedef S SA[5];
// C++
struct S { ... };
typedef S SA[5];
typedef S SA_slice;
class SA_forany { ... };
SA s;
// ...initialize s...
Any a;
a <<= s; // line 1
a <<= SA_forany(s); // line 2
Line 1 results in the invocation of the noncopying 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 noncopying version of operator<<=
for object references takes the address of the T_ptr
type, as follows:
// IDL
interface T { ... };
// C++
void operator<<=(Any&, T_ptr); // copying
void operator<<=(Any&, T_ptr*); // non-copying
The noncopying object reference insertion consumes the object reference pointed to by T_ptr*
; therefore, after insertion the caller may not access the object referred to by T_ptr
because the Any may have duplicated and then immediately released the original object reference. The caller maintains ownership of the storage for the T_ptr
itself.
The copying version of operator<<=
is also supported on the Any_var
type.
To allow type-safe retrieval of a value from an any
, the ORB provides the following operators for each OMG IDL type T:
// C++
Boolean operator>>=(const Any&, T&);
This function signature suffices for primitive types that are usually passed by value. For values of type T that are too large to be passed by value efficiently, the ORB provides a different signature, as follows:
// C++
Boolean operator>>=(const Any&, T*&);
The first form of this function is used only for the following types:
For all other types, the second form of the function is used.
This “right-shift-assign” operator is used to extract a typed value from an any,
as follows:
// C++
Long value;
Any a;
a <<= Long(42);
if (a >>= value) {
// ... use the value ...
}
In this case, the version of operator>>=
for type Long
determines whether the Any truly does contain a value of type Long
and, if so, copies its value into the reference variable provided by the caller and returns TRUE
. If the Any does not contain a value of type Long
, the value of the caller’s reference variable is not changed, and operator>>=
returns FALSE
.
For nonprimitive types, extraction is done by pointer. For example, consider the following OMG IDL struct:
// IDL
struct MyStruct {
long lmem;
short smem;
};
Such a struct could be extracted from an Any as follows:
// C++
Any a;
// ... a is somehow given a value of type MyStruct ...
MyStruct *struct_ptr;
if (a >>= struct_ptr) {
// ... use the value ...
}
If the extraction is successful, the caller’s pointer points to storage managed by the Any, and operator>>=
returns TRUE
. The caller must not try to delete
or otherwise release this storage. The caller also should not use the storage after the contents of the Any variable are replaced via assignment, insertion, or the replace
function, or after the Any variable is destroyed. Care must be taken to avoid using T_var
types with these extraction operators, since they will try to assume responsibility for deleting the storage owned by the Any.
If the extraction is not successful, the value of the caller’s pointer is set equal to the NULL pointer, and operator>>=
returns FALSE
.
Correct extraction of array types relies on the Array_forany
types described in Arrays.
An example of the OMG IDL is as follows:
// IDL
typedef long A[20];
typedef A B[30][40][50];
// C++
typedef Long A[20];
typedef Long A_slice;
class A_forany { ... };
typedef A B[30][40][50];
typedef A B_slice[40][50];
class B_forany { ... };
Boolean operator>>=(const Any&, A_forany&); // for type A
Boolean operator>>=(const Any&, B_forany&); // for type B
The 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.
Since the Boolean, octet, and char OMG IDL types are not required to map to distinct C++ types, another means of distinguishing them from each other is necessary so that they can be used with the type-safe Any interface. Similarly, since both bounded and unbounded strings map to char*
, another means of distinguishing them must be provided. This is done by introducing several new helper types nested in the Any class interface. For example, this is accomplished as shown below:
// C++
class Any
{
public:
// special helper types needed for boolean, octet,
// char, and bounded string insertion
struct from_boolean {
from_boolean(Boolean b) : val(b) {}
Boolean val;
};
struct from_octet {
from_octet(Octet o) : val(o) {}
Octet val;
};
struct from_char {
from_char(Char c) : val(c) {}
Char val;
};
struct from_string {
from_string(char* s, ULong b,
Boolean nocopy = FALSE) :
val(s), bound(b) {}
char *val;
ULong bound;
};
void operator<<=(from_boolean);
void operator<<=(from_char);
void operator<<=(from_octet);
void operator<<=(from_string);
// special helper types needed for boolean, octet,
// char, and bounded string extraction
struct to_boolean {
to_boolean(Boolean &b) : ref(b) {}
Boolean &ref;
};
struct to_char {
to_char(Char &c) : ref(c) {}
Char &ref;
};
struct to_octet {
to_octet(Octet &o) : ref(o) {}
Octet &ref;
};
struct to_string {
to_string(char *&s, ULong b) : val(s), bound(b) {}
char *&val;
ULong bound;
};
Boolean operator>>=(to_boolean) const;
Boolean operator>>=(to_char) const;
Boolean operator>>=(to_octet) const;
Boolean operator>>=(to_string) const;
// other public Any details omitted
private:
// these functions are private and not implemented
// hiding these causes compile-time errors for
// unsigned char
void operator<<=(unsigned char);
Boolean operator>>=(unsigned char &) const;
};
The ORB provides the overloaded operator<<=
and operator>>=
functions for these special helper types. These helper types are used as shown here:
// C++
Boolean b = TRUE;
Any any;
any <<= Any::from_boolean(b);
// ...
if (any >>= Any::to_boolean(b)) {
// ...any contained a Boolean...
}
char* p = "bounded";
any <<= Any::from_string(p, 8);
// ...
if (any >>= Any::to_string(p, 8)) {
// ...any contained a string<8>...
}
A bound value of 0 (zero) indicates an unbounded string.
For noncopying insertion of a bounded or unbounded string into an Any, the nocopy
flag on the from_string
constructor should be set to TRUE
:
// C++
char* p = string_alloc(8);
// ...initialize string p...
any <<= Any::from_string(p, 8, 1); // any consumes p
Assuming that boolean, char, and octet all map the C++ type unsigned
char
, the private and unimplemented operator<<=
and operator>>=
functions for unsigned
char
cause a compile-time error if straight insertion or extraction of any of the boolean, char, or octet types is attempted:
// C++
Octet oct = 040;
Any any;
any <<= oct; // this line will not compile
any <<= Any::from_octet(oct); // but this one will
Sometimes it is desirable to extract an object reference from an Any as the base Object type. This can be accomplished using a helper type similar to those required for extracting boolean, char, and octet:
// C++
class Any
{
public:
...
struct to_object {
to_object(Object_ptr &obj) : ref(obj) {}
Object_ptr &ref;
;
Boolean operator>>=(to_object) const;
...
};
The to_object
helper type is used to extract an object reference from an Any as the base Object type. If the Any contains a value of an object reference type as indicated by its TypeCode, the extraction function operator>>=(to_object)
explicitly widens its contained object reference to Object and returns TRUE
; otherwise, it returns FALSE. This is the only object reference extraction function that performs widening on the extracted object reference. As with regular object reference extraction, no duplication of the object reference is performed by the to_object
extraction operator.
Under some circumstances the type-safe interface to Any is not sufficient. An example is a situation in which data types are read from a file in binary form and are used to create values of type Any. For these cases, the Any class provides a constructor with an explicit TypeCode and generic pointer:
// C++
Any(TypeCode_ptr tc, void *value, Boolean release = FALSE);
The constructor duplicates the given TypeCode pseudo-object reference. If the release
parameter is TRUE
, the Any object assumes ownership of the storage pointed to by the value
parameter. A caller should make no assumptions about the continued lifetime of the value
parameter once it has been handed to an Any with release=TRUE
, since the Any may copy the value
parameter and immediately free the original pointer. If the release
parameter is FALSE
(the default case), the Any object assumes that the caller manages the memory pointed to by value
. The value
parameter can be a NULL pointer.
The Any class also defines three unsafe operations:
// C++
void replace(
TypeCode_ptr,
void *value,
Boolean release = FALSE
);
TypeCode_ptr type() const;
const void *value() const;
The replace
function is intended to be used with types that cannot be used with the type-safe insertion interface, and so is similar to the constructor described above. The existing TypeCode is released and value storage is deallocated, if necessary. The TypeCode function parameter is duplicated. If the release
parameter is TRUE
, the Any object assumes ownership for the storage pointed to by the value
parameter. The Any should make no assumptions about the continued lifetime of the value
parameter once it has been handed to the Any::replace
function with release=TRUE
, since the Any may copy the value
parameter and immediately free the original pointer. If the release
parameter is FALSE
(the default case), the Any object assumes that the caller manages the memory occupied by the value. The value
parameter of the replace
function can be a NULL pointer.
Note that neither the constructor shown above nor the replace
function is type-safe. In particular, no guarantees are made by the compiler at run time as to the consistency between the TypeCode and the actual type of the void*
argument. The behavior of an ORB implementation when presented with an Any
that is constructed with a mismatched TypeCode and value is not defined.
The type
function returns a TypeCode_ptr
pseudo-object reference to the TypeCode associated with the Any. Like all object reference return values, the caller must release the reference when it is no longer needed, or assign it to a TypeCode_var
variable for automatic management.
The value
function returns a pointer to the data stored in the Any. If the Any has no associated value, the value
function returns a NULL pointer.
The default constructor creates an Any with a TypeCode of type tk_null
, and no value. The copy constructor calls _duplicate
on the TypeCode_ptr
of its Any parameter and deep-copies the parameter’s value. The assignment operator releases its own TypeCode_ptr
and deallocates storage for the current value if necessary, then duplicates the TypeCode_ptr
of its Any parameter and deep-copies the parameter’s value. The destructor calls release
on the TypeCode_ptr
and deallocates storage for the value, if necessary.
Other constructors are described in the section Handling Untyped Values.
The full definition of the Any class can be found in the section Any Class Member Functions.
This section is based on information contained in Chapters 3, 5, and 6 of the Common Object Request Broker: Architecture and Specification, Revision 2.4.2, February 2001, and the CORBA C++ Language Mapping Specification, June 1999, published by the Object Management Group (OMG). Used with permission of the OMG.
Objects, more specifically, interface types that objects support, are defined in an IDL interface, allowing arbitrary implementations. There is great value in having a distributed object system that places almost no constraints on implementation. However, there are many occasions in which it is desirable to be able to pass an object by value, rather than by reference. This may be particularly useful when an object’s primary “purpose” is to encapsulate data, or an application explicitly wishes to make a “copy” of an object.
The semantics of passing an object by value are similar to that of standard programming languages. The receiving side of a parameter passed by value receives a description of the “state” of the object. It then instantiates a new instance with that state but having a separate identity from that of the sending side. Once the parameter passing operation is complete, no relationship is assumed to exist between the two instances.
Because it is necessary for the receiving side to instantiate an instance, it must necessarily know something about the object’s state and implementation. Thus, valuetype(s) provide semantics that bridge between CORBA structs and CORBA interfaces, as follows:
The basic notion of valuetypes is relatively simple. A valuetype is, in some sense, half way between a “regular” IDL interface type and a struct. The use of valuetype is a signal from the application programmer that some additional properties (state) and implementation details be specified beyond that of an interface type. Specification of this information puts some additional constraints on the implementation choices beyond that of interface types. This is reflected in both the semantics specified herein, and in the language mappings.
Prior to supporting valuetypes (objects passable by value), all CORBA objects had object references. When multiple clients invoked on a particular object, they use the same object reference. The instance(s) of the object remained on the server ORB and its state was maintained by the server ORB, not the client ORB.
Valuetypes represent a significant addition to the CORBA architecture. As with objects passed by reference, valuetypes have state and methods, but do not have object references and are always invoked locally as programming language objects. Upon request from the receiving side, valuetypes package their state in the sending context, send their state “over the wire” to the receiving side, where an instance is created and populated with the transmitted state. The sending side has no further control of the client-side instance. Thus, the receiving side can make subsequent invocations of the instance locally. This model eliminates the delays involved when communicating over the network. These delays can be significant in large networks. The addition of valuetypes enables CORBA implementations to more easily scale to meet large data-handling requirements.
Therefore, an essential property of valuetypes is that their implementations are always local. That is, the explicit use of valuetypes in a concrete programming language is always guaranteed to use a local implementation, and will not require a remote call. They have no identity (their value is their identity) and they are not “registered” with the ORB.
For example, consider the following IDL valuetype taken from the CORBA C++ Language Mapping Specification, June 1999, published by the Object Management Group (OMG):
// IDL
valuetype Example {
short op1();
long op2(in Example x);
private short val1;
public long val2;
private string val3;
private float val4;
private Example val5;
};
The C++ mapping for this valuetype is:
// C++
class Example : public virtual ValueBase {
public:
virtual Short op1() = 0;
virtual Long op2(Example*) = 0;
virtual Long val2() const = 0;
virtual void val2(Long) = 0;
static Example* _downcast(ValueBase*);
protected:
Example();
virtual ~Example();
virtual Short val1() const = 0;
virtual void val1(Short) = 0;
virtual const char* val3() const = 0;
virtual void val3(char*) = 0;
virtual void val3(const char*) = 0;
virtual void val3(const String_var&) = 0;
virtual Float val4() const = 0;
virtual void val4(Float) = 0;
virtual Example* val5() const = 0;
virtual void val5(Example*) = 0;
private:
// private and unimplemented
void operator=(const Example&);
};
class OBV_Example : public virtual Example {
public:
virtual Long val2() const;
virtual void val2(Long);
protected:
OBV_Example();
OBV_Example(Short init_val1, Long init_val2,
const char* init_val3, Float init_val4,
Example* init_val5);
virtual ~OBV_Example();
virtual Short val1() const;
virtual void val1(Short);
virtual const char* val3() const;
virtual void val3(char*);
virtual void val3(const char*);
virtual void val3(const String_var&);
virtual Float val4() const;
virtual void val4(Float);
virtual Example* val5() const;
virtual void val5(Example*);
// ...
};
The memory management rules and member function signatures for a user-defined type depend upon whether the type is fixed-length or variable-length. A user-defined type is variable-length if it is one of the following:
If a type is not on this list, the type is fixed-length.
Automatic variables (vars) are provided to simplify memory management. Vars are provided through a var class that assumes ownership for the memory required for the type and frees the memory when the instance of the var object is destroyed or when a new value is assigned to the var object.
The BEA Tuxedo provides var classes for the following types:
The var classes have common member functions, but may support additional operators depending upon the OMG IDL type. For an OMG IDL type TYPE, the TYPE_var class contains constructors, destructors, assignment operators, and operators to access the underlying TYPE type. An example var class is as follows:
class TYPE_var
{
public:
// constructors
TYPE_var();
TYPE_var(TYPE *);
TYPE_var(const TYPE_var &);
// destructor
~TYPE_var();
// assignment operators
TYPE_var &operator=(TYPE *);
TYPE_var &operator=(const TYPE_var &);
// accessor operators
TYPE *operator->();
TYPE *operator->() const;
TYPE_var_ptr in() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
operator TYPE_ptr;
operator const TYPE_ptr&() const;
operator TYPE_ptr&();
};
The details of the member functions are as follows:
TYPE_var()
TYPE_var
class. The constructor initializes to 0 (zero) the TYPE *
owned by the var class. You may not invoke the operator-> on a TYPE_var
class unless a valid TYPE *
has been assigned to it.
TYPE_var(TYPE * Value);
Value
argument is a pointer to the TYPE to be owned by this var class. This pointer must not be 0 (zero).
TYPE_var(const TYPE_var & From);
From
parameter. When the TYPE_var is destroyed, the copy of the TYPE is released or deleted. The From
parameter specifies the var class that points to the TYPE to be copied.
~TYPE_var();
CORBA::string_free
routine. For object references, this is the CORBA::release
routine. For other types, this may be delete
or a generated static routine used to free allocated memory.
TYPE_var &operator=(TYPE * NewValue);
NewValue
parameter. If the TYPE_var currently owns a TYPE, it is released before assuming ownership of the NewValue
parameter. The NewValue
argument is a pointer to the TYPE to be owned by this var class. This pointer must not be 0 (zero).
TYPE_var &operator=(const TYPE_var &From);
From
TYPE_var parameter. If TYPE_var currently owns a TYPE, it is released. When the TYPE_var is destroyed, the copy of the TYPE is released. The From
parameter specifies the var class that points to the data to be copied.
TYPE *operator->();
TYPE *operator->() const;
operator->
until the var owns a valid TYPE. Do not try to release this return value or access this return value after the TYPE_var has been destroyed.
TYPE_var_ptr in() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
in
parameter, call the in()
member function; for inout
parameters, the inout()
member function; for out
parameters, the out()
member function. To obtain a return value from the TYPE_var, call the _return()
function. For each TYPE
_var type, the return types of each of these functions will match the type shown in Table 13-7 for the in
, inout
, out
, and return modes for the underlying type TYPE, respectively.
Some differences occur in the operators supported for the user-defined data types. Table 13-3 describes the various operators supported by each OMG IDL data type, in the generated C++ code. Because the assignment operators are supported for all of the data types described in Table 13-3, they are not included in the comparison.
The signatures are as shown in Table 13-4.
Sequence vars support the following additional operator[] member function:
TYPE &operator[](CORBA::ULong Index);
operator[]
of sequence owned by the var class. The operator[]
returns a reference to the appropriate element of the sequence at the specified index. The Index
argument specifies the index of the element to return. This index cannot be greater than the current sequence length.
Array vars do not support operator->, but do support the following additional operator[] member functions to access the array elements:
TYPE_slice& operator[](CORBA::ULong Index);
const TYPE_slice & operator[](CORBA::ULong Index) const;
Index
argument specifies the index of the slice to return. This index cannot be greater than the array dimension.
The String vars in the member functions described in this section and in the section Sequence vars have a TYPE of char *
. String vars support additional member functions, as follows:
String_var(char * str)
String_var
from a string. The str
argument specifies the string that will be assumed. The user must not use the str
pointer to access data.
String_var(const char * str)
String_var(const String_var & var)
String_var
from a const
string. The str
argument specifies the const string that will be copied. The var
argument specifies a reference to the string to be copied.
String_var & operator=(char * str)
CORBA::string_free
, and then assumes ownership of the input string. The str
argument specifies the string whose ownership will be assumed by this String_var
object.
String_var & operator=(const char * str)
String_var & operator=(const String_var & var)
CORBA::string_free
, and then copies the input string. The Data
argument specifies the string whose ownership will be assumed by this String_var
object.
char operator[] (Ulong Index)
char operator[] (Ulong Index) const
Index
argument specifies the index of the array to use in accessing a particular character within the array. Zero-based indexing is used. The returned value of the Char operator[] (Ulong Index)
function can be used as an lvalue. The returned value of the Char operator[] (Ulong Index) const
function cannot be used as an lvalue.
Structured types (struct, union, sequence), arrays, and interfaces have a corresponding generated _out class. The out class is provided for simplifying the memory management of pointers to variable-length and fixed-length types. For more information about out
classes and the common member functions, see the section Using out Classes.
Some differences occur in the operators supported for the user-defined data types. Table 13-5 describes the various operators supported by each OMG IDL data type, in the generated C++ code. Because the assignment operators are supported for all of the data types described in Table 13-3, they are not included in the comparison.
The signatures are as shown in Table 13-6.
When a TYPE
_var
is passed as an out
parameter, any previous value it referred to must be implicitly deleted. To give the ORB enough hooks to meet this requirement, each T_var
type has a corresponding TYPE
_out
type that is used solely as the out
parameter type.
Note: | The _out classes are not intended to be instantiated directly by the programmer. Specify an _out class only in function signatures. |
The general form for TYPE
_out
types for variable-length types is as follows:
// C++
class TYPE_out
{
public:
TYPE_out(TYPE*& p) : ptr_(p) { ptr_ = 0; }
TYPE_out(TYPE_var& p) : ptr_(p.ptr_) { delete ptr_; ptr_ = 0;}
TYPE_out(TYPE_out& p) : ptr_(p.ptr_) {}
TYPE_out& operator=(TYPE_out& p) { ptr_ = p.ptr_;
return *this;
}
Type_out& operator=(Type* p) { ptr_ = p; return *this; }
operator Type*&() { return ptr_; }
Type*& ptr() { return ptr_; }
Type* operator->() { return ptr_; }
private:
Type*& ptr_;
// assignment from
TYPE
_var not allowed
void operator=(const TYPE_var&):
};
The first constructor binds the reference data member with the T*&
argument and sets the pointer to the zero (0) pointer value. The second constructor binds the reference data member with the pointer held by the
TYPE
_var
argument, and then calls delete
on the pointer (or string_free()
in the case of the String_out
type or TYPE
_free()
in the case of a TYPE
_var
for an array type TYPE
). The third constructor, the copy constructor, binds the reference data member to the same pointer referenced by the data member of the constructor argument.
Assignment from another
TYPE
_out
copies the TYPE
*
referenced by the TYPE
_out
argument to the data member. The overloaded assignment operator for TYPE
*
simply assigns the pointer argument to the data member. Note that assignment does not cause any previously held pointer to be deleted; in this regard, the TYPE
_out
type behaves exactly as a TYPE
*
. The TYPE
*&
conversion operator returns the data member. The ptr()
member function, which can be used to avoid having to rely on implicit conversion, also returns the data member. The overloaded arrow operator (operator->()
) allows access to members of the data structure pointed to by the TYPE
*
data member. Compliant applications may not call the overloaded operator->()
unless the TYPE
_out
has been initialized with a valid nonNULL
TYPE
*
.
Assignment to a TYPE
_out
from instances of the corresponding TYPE
_var
type is disallowed because there is no way to determine whether the application developer wants a copy to be performed, or whether the TYPE
_var
should yield ownership of its managed pointer so it can be assigned to the TYPE
_out
. To perform a copy of a TYPE
_var
to a TYPE
_out
, the application should use new
, as follows:
// C++
TYPE
_var t = ...;
my_out = newTYPE
(t.in()); // heap-allocate a copy
The in()
function called on t
typically returns a const
TYPE
&
, suitable for invoking the copy constructor of the newly allocated T
instance.
Alternatively, to make the TYPE
_var
yield ownership of its managed pointer so it can be returned in a T_out
parameter, the application should use the TYPE
_var::_retn()
function, as follows:
// C++
TYPE
_var t = ...;
my_out = t._retn(); // t yields ownership, no copy
Note that the TYPE
_out
types are not intended to serve as general-purpose data types to be created and destroyed by applications; they are used only as types within operation signatures to allow necessary memory management side-effects to occur properly.
When a _var
is passed as an out
parameter, any previous value it refers to must be implicitly released. To give C++ mapping implementations enough hooks to meet this requirement, each object reference type results in the generation of an _out
type that is used solely as the out
parameter type. For example, interface TYPE
results in the object reference type TYPE
_ptr
, the helper type TYPE
_var
, and the out
parameter type TYPE
_out
. The general form for object reference _out
types is as follows:
// C++
classTYPE
_out
{
public:
TYPE
_out(
TYPE
_ptr& p) : ptr_(p) { ptr_ =
TYPE
::_nil(); }
TYPE
_out(
TYPE
_var& p) : ptr_(p.ptr_) {
release(ptr_); ptr_ =TYPE
::_nil();
}
TYPE
_out(
TYPE
_out& a) : ptr_(a.ptr_) {}
TYPE
_out& operator=(
TYPE
_out& a) {
ptr_ = a.ptr_; return *this;
}
TYPE
_out& operator=(const
TYPE
_var& a) {
ptr_ =TYPE
::_duplicate(
TYPE
_ptr(a)); return *this;
}
TYPE
_out& operator=(TYPE_ptr p) { ptr_ = p; return *this; }
operatorTYPE
_ptr&() { return ptr_; }
TYPE
_ptr& ptr() { return ptr_; }
TYPE
_ptr operator->() { return ptr_; }
private:
TYPE
_ptr& ptr_;
};
Sequence outs support the following additional operator[]
member function:
TYPE &operator[](CORBA::ULong Index);
operator[]
of the sequence owned by the out class. The operator[]
returns a reference to the appropriate element of the sequence at the specified index. The Index
argument specifies the index of the element to return. This index cannot be greater than the current sequence length.
Array outs do not support operator->
, but do support the following additional operator[]
member functions to access the array elements:
TYPE_slice& operator[](CORBA::ULong Index);
const TYPE_slice & operator[](CORBA::ULong Index) const;
Index
argument specifies the index of the slice to return. This index cannot be greater than the array dimension.
When a String_var
is passed as an out
parameter, any previous value it refers to must be implicitly freed. To give C++ mapping implementations enough hooks to meet this requirement, the string type also results in the generation of a String_out
type in the CORBA namespace that is used solely as the string out
parameter type. The general form for the String_out
type is as follows:
// C++
class String_out
{
public:
String_out(char*& p) : ptr_(p) { ptr_ = 0; }
String_out(String_var& p) : ptr_(p.ptr_) {
string_free(ptr_); ptr_ = 0;
}
String_out(String_out& s) : ptr_(s.ptr_) {}
String_out& operator=(String_out& s) {
ptr_ = s.ptr_; return *this;
}
String_out& operator=(char* p) {
ptr_ = p; return *this;
}
String_out& operator=(const char* p) {
ptr_ = string_dup(p); return *this;
}
operator char*&() { return ptr_; }
char*& ptr() { return ptr_; }
private:
char*& ptr_;
// assignment from String_var disallowed
void operator=(const String_var&);
};
The first constructor binds the reference data member with the char*&
argument. The second constructor binds the reference data member with the char*
held by the String_var
argument, and then calls string_free()
on the string. The third constructor, the copy constructor, binds the reference data member to the same char*
bound to the data member of its argument.
Assignment from another String_out
copies the char*
referenced by the argument String_out
to the char*
referenced by the data member. The overloaded assignment operator for char*
simply assigns the char*
argument to the data member. The overloaded assignment operator for const
char*
duplicates the argument and assigns the result to the data member. Note that the assignment does not cause any previously held string to be freed; in this regard, the String_out
type behaves exactly as a char*
. The char*&
conversion operator returns the data member. The ptr()
member function, which can be used to avoid having to rely on implicit conversion, also returns the data member.
Assignment from String_var
to a String_out
is disallowed because of the memory management ambiguities involved. Specifically, it is not possible to determine whether the string owned by the String_var
should be taken over by the String_out
without copying, or if it should be copied. Disallowing assignment from String_var
forces the application developer to make the choice explicitly, as follows:
// C++
void
A::op(String_out arg)
{
String_var s = string_dup("some string");
...
out = s; // disallowed; either
out = string_dup(s); // 1: copy, or
out = s._retn(); // 2: adopt
}
On the line marked with the comment “1,” the caller is explicitly copying the string held by the String_var
and assigning the result to the out
argument. Alternatively, the caller could use the technique shown on the line marked with the comment “2” to force the String_var
to give up its ownership of the string it holds so that it may be returned in the out
argument without incurring memory management errors.
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, the BEA Tuxedo includes out
parameters because the parameter could contain the result from a previous call. The provision of the T_out
types is intended to give implementations the hooks necessary to free the inaccessible storage while converting from the T_var
types. The following examples demonstrate the compliant behavior:
// IDL
struct S { string name; float age; };
void f(out S p);
// C++
S_var s;
f(s);
// use s
f(s); // first result will be freed
S *sp; // need not initialize before passing to out
f(sp);
// use sp
delete sp; // cannot assume next call will free old value
f(sp);
Note that implicit deallocation of previous values for out
and inout
parameters works only with T_var
types, not with other types:
// IDL
void q(out string s);
// C++
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:
// C++
char *s;
String_var svar;
for (int i = 0 ; i < 10; i++) {
q(s);
string_free(s); // explicit deallocation
// OR:
q(svar); // implicit deallocation
}
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
interface A;
void f(inout string s, inout A obj);
// C++
void Aimpl::f(char *&s, A_ptr &obj) {
String_var s_tmp = s;
s = /* new data */;
A_var obj_tmp = obj;
obj = /* new reference */
}
For parameters that are passed or returned as a pointer (T*
) or as a reference to a pointer (T*&
), an application is not allowed to pass or return a NULL pointer; the result of doing so is undefined. In particular, a caller may not pass a NULL pointer under any of the following circumstances:
However, a caller may pass a reference to a pointer with a NULL value for out
parameters, because the callee does not examine the value, but overwrites it. A callee may not return a NULL pointer under any of the following circumstances:
Table 13-7 displays the mapping for the basic OMG IDL parameter passing modes and return type according to the type being passed or returned. Table 13-8 displays the same information for T_var
types. Table 13-8 is merely for informational purposes; it is expected that operation signatures for both clients and servers will be written in terms of the parameter-passing modes shown in Table 13-7, with the exception that the T_out
types will be used as the actual parameter types for all out
parameters.
It is also expected that T_var
types will support the necessary conversion operators to allow them to be passed directly. Callers should always pass instances of either T_var
types or the base types shown in Table 13-7, and callees should treat their T_out
parameters as if they were actually the corresponding underlying types shown in Table 13-7.
In Table 13-7, 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 array
specified except the first dimension.
Note: | The Object reference ptr data type includes pseudo-object references. The array slice return is an array with all the dimensions of the original array except the first dimension. |
A caller is responsible for providing storage for all arguments passed as in
arguments.
Note: | The object reference var data type includes pseudo-object references. |
Table 13-9 and Table 13-10 describe the caller’s responsibility for storage associated with inout
and out
parameters and for return results.
1In particular, exception
used as a data type and a function name.