CORBA Programming Reference

     Previous  Next    Open TOC in new window  Open Index in new window  View as PDF - New Window  Get Adobe Reader - New Window
Content starts here

Mapping of OMG IDL Statements to C++

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.

 


Mappings

OMG IDL-to-C++ mappings are described for the following:

In addition, the following topics are discussed:

Data Types

Each OMG IDL data type is mapped to a C++ data type or class.

Basic Data Types

The basic data types in OMG IDL statements are mapped to C++ typedefs in the CORBA module, as shown in Table 13-1.

Table 13-1 Basic OMG IDL and C++ Data Types 
OMG IDL
C++
C++ Out Type
short
CORBA::Short
CORBA::Short_out
long
CORBA::Long
CORBA::Long_out
unsigned short
CORBA::UShort
CORBA::UShort_out
unsigned long
CORBA::ULong
CORBA::ULong_out
float
CORBA::Float
CORBA::Float_out
double
CORBA::Double
CORBA::Double_out
char
CORBA::Char
CORBA::Char_out
boolean
CORBA::Boolean
CORBA::Boolean_out
octet
CORBA::Octet
CORBA::Octet_out
wchar
CORBA::WChar
CORBA::WChart_out

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.

Complex Data Types

Object, pseudo-object, and user-defined types are mapped as shown in Table 13-2.

Table 13-2 Object, Pseudo-object, and User-defined OMG IDL and C++ Types
OMG IDL
C++
Object
CORBA::Object_ptr
struct
C++ struct
union
C++ class
enum
C++ enum
string
char *
wstring
CORBA::WChar *
sequence
C++ class
array
C++ array

The mapping for strings and UDTs is described in more detail in the following sections.

Strings

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.

wchars

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”

A code example for wchar is:

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.

wstrings

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.

Constants

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;

Enums

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;

Structs

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.

Fixed-length Versus Variable-length Structs

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);
};

};

Member Mapping

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.

Var

A var class is generated for structs. For more information, see the section Using var Classes.

Out

An out class is generated for structs. For more information, see the section Using out Classes.

Unions

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.

Union Member Accessor and Modifier Member Function Mapping

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:

Var

A var class is generated for a union. For more information, see the section Using var Classes .

Out

An out class is generated for a union. For more information, see the section Using out Classes.

Member Functions

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();

This is the default constructor for a union. No default discriminator is set by this function, so you cannot access the union until you set the value of the union.

TYPE( const TYPE & From);

This copy constructor deep copies the specified union. Any data in the union parameter is copied. The From argument specifies the union to be copied.

~TYPE();

This destructor frees the data associated with the union.

TYPE &operator=(const TYPE & From);

This assignment operator copies the specified union. Any existing value in the current union is freed. The From argument specifies the union to be copied.

void _d (CORBA::Long Discrim);

This modifier function sets the value of the union discriminant. The 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.
Only use this function to set the discriminant to a value within the same union member. You cannot use this function to implicitly switch between different union members. These restrictions are illustrated by the following code:
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
When the _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;

This function returns the current discriminant value. The data type of the return value 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.

Sequences

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 *);
};
    };

Sequence Element Mapping

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.

Vars

A var class is generated for a sequence. For more information, see the section Using var Classes.

Out

An out class is generated for a sequence. For more information, see the section Using out Classes.

Member Functions

For a given OMG IDL sequence SEQ with base type TYPE, the member functions for the generated sequence class are described as follows:

SEQ ();

This is the default constructor for a sequence. The length is set to 0 (zero). If the sequence is unbounded, the maximum is also set to 0 (zero). If the sequence is bounded, the maximum is specified by the OMG IDL type and cannot be changed.

SEQ (CORBA::ULong Max);

This constructor is present only if the sequence is unbounded. This function sets the length of the sequence to 0 (zero) and sets the maximum of the buffer to the specified value. The Max argument specifies the maximum length of the sequence.

SEQ (CORBA::ULong Max, CORBA::ULong Length, TYPE * Value,
CORBA::Boolean Release);

This constructor sets the maximum, length, and elements of the sequence. The Release flag determines whether elements are released when the sequence is destroyed. Explanations of the arguments are as follows:

Max

The maximum value of the sequence. This argument is not present in bounded sequences.

Length

The current length of the sequence. For bounded sequences, this value must be less than the maximum specified in the OMG IDL type.

Value

A pointer to the buffer containing the elements of the sequence.

Release

Determines whether elements are released. If this flag has a value of 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);

This copy constructor deep copies the sequence from the specified argument. The From argument specifies the sequence to be copied.

~SEQ();

This destructor frees the sequence and, depending upon the Release flag, may free the sequence elements.

SEQ& operator=(const SEQ& From);

This assignment operator deep copies the sequence from the specified sequence argument. Any existing elements in the current sequence are released if the Release flag in the current sequence is set to CORBA_TRUE. The From argument specifies the sequence to be copied.

CORBA::ULong maximum( ) const;

This function returns the maximum of the sequence. For a bounded sequence, this is the value set in the OMG IDL type. For an unbounded sequence, this is the current maximum of the sequence.

void length(CORBA::ULong Length);

This function sets the current length of the sequence. The 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.
For a bounded sequence, the length cannot be set to a value greater than the maximum.

CORBA::ULong length() const;

This function returns the current length of the sequence.

TYPE & operator[](CORBA::ULong Index);
const
TYPE & operator[](CORBA::ULong Index) const;

These accessor functions return a reference to the sequence element 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. 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);

This static function allocates a buffer to be used with the TYPE * constructor. The NumElems argument specifies the number of elements in the buffer to allocate. If the buffer cannot be allocated, NULL is returned.
If this buffer is not passed to the TYPE * constructor with release set to CORBA_TRUE, it should be freed using the freebuf member function.

static void freebuf(TYPE * Value);

This static function frees a 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.

Arrays

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);

};

Array Slice

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.

Array Element Mapping

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.

Vars

A var class is generated for an array. For more information, see the section Using var Classes.

Out

An out class is generated for an array. For more information, see the section Using out Classes.

Allocation Member Functions

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);

This function allocates a 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);

This function frees a dynamically allocated TYPE array. The Value argument is a pointer to the dynamically allocated TYPE array to be freed.

Exceptions

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.

Member Mapping

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.

Var

A var class is generated for an exception. For more information, see the section Using var Classes.

Out

An out class is generated for an exception. For more information, see the sectionUsing out Classes.

Member Functions

For a given OMG IDL exception TYPE, the generated member functions are as follows:

static TYPE * _narrow(CORBA::Exception_ptr Except);

This function returns a pointer to a 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 ( );

This is the default constructor for the exception. No initialization of members is performed for fixed-length members. Variable-length members map to self-managing types; these types have constructors that initialize the member.

TYPE(member-parameters);

This constructor has an argument for each of the members in the exception. The constructor copies each argument and does not assume ownership of the memory for any argument. Building on the previous example, the signature of the constructor is:
NonExist (ID _BadId);
There is one argument for each member of the exception. The type and parameter-passing mechanism are identical to the Any insertion operator. For information about the Any insertion operator, see the section Insertion into Any.

TYPE (const TYPE & From);

This copy constructor copies the data from the specified TYPE exception argument. The From argument specifies the exception to be copied.

~TYPE ();

This destructor frees the data associated with the exception.

TYPE & operator=(const TYPE & From);

This assignment operator copies the data from the specified TYPE exception argument. The From argument specifies the exception to be copied.

void _raise ();

This function causes the exception instance to throw itself. A catch clause can catch it by a more derived type.

Mapping of Pseudo-objects to C++

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.

Usage

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:

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.

Mapping Rules

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:

Relation to the C PIDL Mapping

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:

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.

Typedefs

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:

Implementing Interfaces

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)
{
. . .
}

Argument Mapping

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.

Implementing Operations

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.

Skeleton Derivation from Object

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

PortableServer Functions

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.

Modules

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 Oracle 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.

Interfaces

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.

Generated Static Member Functions

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)

This static member function duplicates an existing INTF object reference and returns a new INTF object reference. The new INTF object reference must be released by calling the 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)

This static member function returns a new INTF object reference given an existing 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.
The 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 ( )

This static member function returns the new nil object reference for the INTF interface. The new reference does not have to be released by calling the CORBA::release member function.

Object Reference Types

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.

Attributes

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)
{
}

Argument Mapping

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.

Any Type

An any in OMG IDL is mapped to the CORBA::Any class. The CORBA::Any class handles C++ types in a type-safe manner.

Handling Typed Values

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:

Insertion into Any

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:

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.

Extraction from Any

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.

Distinguishing Boolean, Octet, Char, and Bounded Strings

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

Widening to Object

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.

Handling Untyped Values

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.

Any Constructors, Destructor, Assignment Operator

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 Any Class

The full definition of the Any class can be found in the section Any Class Member Functions.

Value Type

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.

Overview

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:

Architecture

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.

Benefits

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.

Valuetype Example

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*);
      // ...
};

 


Fixed-length Versus Variable-length User-defined Types

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.

 


Using var Classes

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 Oracle 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 const TYPE_ptr&() const;
operator TYPE_ptr&();
operator TYPE_ptr;
};

The details of the member functions are as follows:

TYPE_var()

This is the default constructor for the 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);

This constructor assumes ownership of the specified TYPE * parameter. When the TYPE_var is destroyed, the TYPE is released. The 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);

This copy constructor allocates a new TYPE and makes a deep copy of the data contained in the TYPE owned by the 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();

This destructor uses the appropriate mechanism to release the TYPE owned by the var class. For strings, this is the 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);

This assignment operator assumes ownership of the TYPE pointed to by the 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);

This assignment operator allocates a new TYPE and makes a deep copy of the data contained in the TYPE owned by the 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;

These operators return a pointer to the TYPE owned by the var class. The var class continues to own the TYPE and it is the responsibility of the var class to release TYPE. You cannot use the 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();

Because implicit conversions can sometimes cause a problem with some C++ compilers and with code readability, the TYPE_var types also support member functions that allow them to be explicitly converted for purposes of parameter passing. To pass a TYPE_var and an 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.

Table 13-3  Comparison of Operators Supported for User-defined Data Type var Classes
OMG IDL Data Type
operator ->
operator[ ]
struct
Yes
No
union
Yes
No
sequence
Yes
Yes, non-const only
array
No
Yes

The signatures are as shown in Table 13-4.

Table 13-4 Operator Signatures for _var Classes
OMG IDL Data Type
Operator Member Functions
struct
TYPE * operator-> ()
TYPE * operator-> () const
union
TYPE * operator-> ()
TYPE * operator-> () const
sequence
TYPE * operator-> ()
TYPE * operator-> () const
TYPE & operator[](CORBA::Long index)
array
TYPE_slice & operator[](CORBA::Long index) TYPE_slice & operator[](CORBA::Long index) const

Sequence vars

Sequence vars support the following additional operator[] member function:

TYPE &operator[](CORBA::ULong Index);

This operator invokes the 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

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;

These operators return a reference to the array slice at the specified index. An array slice 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. The Index argument specifies the index of the slice to return. This index cannot be greater than the array dimension.

String vars

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)

This constructor makes a 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)

This constructor makes a 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)

This assignment operator first releases the contained string using 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)

This assignment operator first releases the contained string using 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

These array operators are superscripting operators that provide access to characters within the string. The 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.

out Classes

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-5, they are not included in the comparison.

Table 13-5 Comparison of Operators Supported for User-defined Data Type Out Classes
OMG IDL Data Type
operator ->
operator[ ]
struct
Yes
No
union
Yes
No
sequence
Yes
Yes, non-const only
array
No
Yes

The signatures are as shown in Table 13-6.

Table 13-6 Operator Signatures for _out Classes
OMG IDL Data Type
Operator Member Functions
struct
TYPE * operator-> ()
TYPE * operator-> () const
union
TYPE * operator-> ()
TYPE * operator-> () const
sequence
TYPE * operator-> ()
TYPE * operator-> () const
TYPE & operator[](CORBA::Long index)
array
TYPE_slice & operator[](CORBA::Long index) TYPE_slice & operator[](CORBA::Long index) const

 


Using out Classes

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 = new
TYPE(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.

Object Reference out Parameter

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++
class
TYPE_out
{
public:
TYPE_out(TYPE_ptr& p) : ptr_(p) { ptr_ = TYPE::_nil(); }
TYPE_out(TYPE_var& p) : ptr_(p.ptr_) {
release(ptr_); ptr_ =
TYPE::_nil();
}
TYPE_out(TYPE_out& a) : ptr_(a.ptr_) {}
TYPE_out& operator=(TYPE_out& a) {
ptr_ = a.ptr_; return *this;
}
TYPE_out& operator=(const TYPE_var& a) {
ptr_ =
TYPE::_duplicate(TYPE_ptr(a)); return *this;
}
TYPE_out& operator=(TYPE_ptr p) { ptr_ = p; return *this; }
operator
TYPE_ptr&() { return ptr_; }
TYPE_ptr& ptr() { return ptr_; }
TYPE_ptr operator->() { return ptr_; }

private:
TYPE_ptr& ptr_;
};

Sequence outs

Sequence outs support the following additional operator[] member function:

TYPE &operator[](CORBA::ULong Index);

This operator invokes the 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

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;

These operators return a reference to the array slice at the specified index. An array slice 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. The Index argument specifies the index of the slice to return. This index cannot be greater than the array dimension.

String outs

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.

 


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, the Oracle 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:

Operation Parameters and Signatures

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.

Table 13-7 Basic Argument and Result Passing 
Data Type
In
Inout
Out
Return
short
Short
Short&
Short&
Short
long
Long
Long&
Long&
Long
unsigned short
UShort
UShort&
UShort&
UShort
unsigned long
ULong
ULong&
ULong&
ULong
float
Float
Float&
Float&
Float
double
Double
Double&
Double&
Double
boolean
Boolean
Boolean&
Boolean&
Boolean
char
Char
Char&
Char&
Char
wchar
WChar
WChar&
WChar
Octet
octet
Octet
Octet&
Octet&
Octet
enum
enum
enum&
enum&
enum
object reference ptr (See Note below.)
objref_ptr
objref_ptr&
objref_ptr&
objref_ptr
struct, fixed
const struct&
struct&
struct&
struct
struct, variable
const struct&
struct&
struct*&
struct*
union, fixed
const union&
union&
union&
union
union, variable
const union&
union&
union*&
union*
string
const char*
char*&
char*&
char*
wstring
const WChar
WChar*&
Wchar*&
WChar*
sequence
const sequence&
sequence&
sequence*&
sequence*
array, fixed
const array
array
array
array slice* (See Note below.)
array, variable
const array
array
array slice*&
array slice*
any
const any&
any&
any*&
any*

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.

Table 13-8 T_var Argument and Result Passing
Data Type
In
Inout
Out
Return
object reference var (See Note below.)
const objref_var&
objref_var&
objref_var&
objref_var
struct_var
const struct_var&
struct_var&
struct_var&
struct_var
union_var
const union_var&
union_var&
union_var&
union_var
string_var
const string_var&
string_var&
string_var&
string_var
sequence_var
const sequence_var&
sequence_var&
sequence_var&
sequence_var
array_var
const array_var&
array_var&
array_var&
array_var
any_var
const any_var&
any_var&
any_var&
any_var

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.

Table 13-9 Caller Argument Storage Responsibilities 
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
wchar
1
1
1
octet
1
1
1
enum
1
1
1
object reference ptr
2
2
2
struct, fixed
1
1
1
struct, variable
1
3
3
union, fixed
1
1
1
union, variable
1
3
3
string
4
3
3
wstring
4
3
3
sequence
5
3
3
array, fixed
1
1
6
array, variable
1
6
6
any
5
3
3

Table 13-10 Argument Passing Cases 
Case
 
1
Caller allocates all necessary storage, except that which may be encapsulated and managed within the parameter itself. For inout parameters, the caller provides the initial value, and the callee may change that value. For out parameters, the caller allocates the storage but need not initialize it, and the callee sets the value. Function returns are by value.
2
Caller allocates storage for the object reference. For inout parameters, the caller provides an initial value; if the callee wants to reassign the inout parameter, it will first call CORBA::release on the original input value. To continue to use an object reference passed in as an inout, the caller must first duplicate the reference. The caller is responsible for the release of all out and return object references. Release of all object references embedded in other structures is performed automatically by the structures themselves.
3
For out parameters, the caller allocates a pointer and passes it by reference to the callee. The callee sets the pointer to point to a valid instance of the parameter’s type. For returns, the callee returns a similar pointer. The callee is not allowed to return a NULL pointer in either case.
In both cases, the caller is responsible for releasing the returned storage. To maintain local/remote transparency, the caller must always release the returned storage, regardless of whether the callee is located in the same address space as the caller or is located in a different address space. Following the completion of a request, the caller is not allowed to modify any values in the returned storage—to do so, the caller must first copy the returned instance into a new instance, and modify the new instance.
4
For inout strings, the caller provides storage for both the input string and the char* pointing to it. Since the callee may deallocate the input string and reassign the char* to point to new storage to hold the output value, the caller should allocate the input string using string_alloc(). The size of the out string is, therefore, not limited by the size of the in string. The caller is responsible for deleting the storage for the out using string_free(). The callee is not allowed to return a NULL pointer for an inout, out, or return value.
5
For inout sequences and anys, assignment or modification of the sequence or any may cause deallocation of owned storage before any reallocation occurs, depending upon the state of the Boolean release parameter with which the sequence or any was constructed.
6
For out parameters, the caller allocates a pointer to an array slice, which has all the same dimensions of the original array except the first, and passes the pointer by reference to the callee. The callee sets the pointer to point to a valid instance of the array.
For returns, the callee returns a similar pointer. The callee is not allowed to return a NULL pointer in either case. In both cases, the caller is responsible for releasing the returned storage.
To maintain local/remote transparency, the caller must always release the returned storage, regardless of whether the callee is located in the same address space as the callee or is located in a different address space. Following completion of a request, the caller is not allowed to modify any values in the returned storage—to do so, the caller must first copy the returned array instance into a new array instance, and modify the new instance.

1In particular, exception used as a data type and a function name.


  Back to Top       Previous  Next