|
|
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: Architecture and Specification. Revision 2.2, February 1998, published by the Object Management Group (OMG). Used with permission by OMG.
OMG IDL-to-C++ mappings are described for the following:
Mappings
This chapter also describes the generated var classes for user-defined data types.
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.
Data Types
Basic Data Types
OMG IDL |
C++ |
C++ Out Type |
---|---|---|
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.
Complex Data Types
OMG IDL |
C++ |
---|---|
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:
Strings
Note: The string_alloc function allocates len+1 characters so that the resulting string has enough space to hold a trailing NULL character.
For more information about string member functions, see the section "Strings" on page 1-119 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 interface Order This definition maps to C++ as follows:
// C++ const char *const class Order : public virtual CORBA::Object 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 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; 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" on page 13-49.
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 This definition maps to C++ as follows:
// C++ class INVENT struct Address 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 on page 13-2. For object references, pseudo-object references, and strings, the member is mapped to the appropriate var class:
Constants
{
const string Name = "Inventory Modules";
{
const long MAX_ORDER_NUM = 10000;
};
};
CompanyName = "BEA Systems Incorporated";
. . .
class INVENT
{
static const char *const Name;
. . .
{
static const CORBA::Long MAX_ORDER_NUM;
. . .
};
}; Enums
{
enum Reply {ACCEPT, REFUSE};
}
{
. . .
};
accept_reply = INVENT::ACCEPT; Structs
Fixed-Length Versus Variable-Length Structs
{
// Fixed-length
struct Date
{
long year;
long month;
long day;
};
// Variable-length
struct Address
{
string aptNum;
string streetName;
string city;
string state;
string zipCode;
};
};
{
struct Date
{
CORBA::Long year;
CORBA::Long month;
CORBA::Long day;
};
{
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
All other data types are mapped as shown in Table 13-2, "Object, Pseudo-Object, and User-Defined OMG IDL and C++ Types," on page 13-3.
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" on page 13-49.
An out class is generated for structs. For more information, see the section "Using out Classes" on page 13-56.
A union in OMG IDL is mapped to a C++ class. The C++ class contains the following:
Var
Out
Unions
For example, consider the following OMG IDL definition:
// OMG IDL union OrderItem switch (long) This definition maps to C++ as follows:
// C++ class OrderItem void _d (CORBA::Long); 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); 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:
{
case 1: itemStruct itemInfo;
case 2: orderStruct orderInfo;
default: ID idInfo;
};
{
public:
OrderItem();
OrderItem(const OrderItem &);
~OrderItem();
OrderItem &operator=(const OrderItem&);
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;
. . .
}; Union Member Accessor and Modifier Member Function Mapping
ID idInfo () const;
The following example generates two member functions for a basic data type with member name basictype
:
void basictype (
TYPE
); // modifier
TYPE
basictype () const; // accessor
For the mapping from an OMG IDL data type to the C++ data type TYPE, see Table 13-1, "Basic OMG IDL and C++ Data Types," on page 13-2.
For object and Typecode types with member name objtype
, member functions are generated as follows:
void objtype (
TYPE); // modifier
TYPE
objtype () const; // accessor
For the mapping from an OMG IDL data type to the C++ data type TYPE, see Table 13-1, "Basic OMG IDL and C++ Data Types," on page 13-2.
The modifier member function does not assume ownership of the specified object reference argument. Instead, the modifier duplicates the object reference or pseudo-object reference. You are responsible for releasing the reference when it is no longer required.
For an enum TYPE with member name enumtype
, member functions are generated as follows:
void enumtype (
TYPE
); // modifier
TYPE
enumtype () const; // accessor
For strings, one accessor and three modifier functions are generated, as follows:
void stringInfo (char *); // modifier 1
void stringInfo (const char *); // modifier 2
void stringInfo (const CORBA::String_var &); // modifier 3
const char * stringInfo () const; // accessor
The first modifier assumes ownership of the 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.
The second and third modifiers make a copy of the specified string passed in the parameter or contained in the string var.
The accessor function returns a pointer to internal memory of the union; do not attempt to free this memory, and do not access this memory after the union value has been changed or the union has been destroyed.
For these data types, modifier and accessor functions are generated with references to the type
, as follows:
void reftype (
TYPE
&); // modifier
const
TYPE
& reftype () const; // accessor
TYPE
& reftype (); // accessor
The modifier function does not assume ownership of the input type
parameter; instead, the function makes a copy of the data type.
For an array, the modifier member function accepts an array pointer while the accessor returns a pointer to an array slice, as follows:
void arraytype (
TYPE
); // modifier
TYPE
_slice * arraytype () const; // accessor
The modifier function does not assume ownership of the input
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" on page 13-49 .
An out class is generated for a union. For more information, see the section "Using out Classes" on page 13-56.
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:
Var
Out
Member Functions
A sequence in OMG IDL is mapped to a C++ class. The C++ class contains the following:
Each sequence has the following:
You must set the length before accessing any elements.
For example, consider the following OMG IDL definition:
// OMG IDL module INVENT This definition maps to C++ as follows:
// C++ class LogList // Maximum constructor // TYPE * data constructor // Copy constructor // Destructor LogList &operator=(const LogList&); CORBA::ULong maximum() const; void length(CORBA::ULong); LogItem &operator[](CORBA::ULong _index); static LogItem *allocbuf(CORBA::ULong _nelems); }; 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, "Basic OMG IDL and C++ Data Types," on page 13-2. 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, "Object, Pseudo-Object, and User-Defined OMG IDL and C++ Types," on page 13-3.
A var class is generated for a sequence. For more information, see the section "Using var Classes" on page 13-49.
An out class is generated for a sequence. For more information, see the section "Using out Classes" on page 13-56.
For a given OMG IDL sequence SEQ with base type TYPE, the member functions for the generated sequence class are described as follows:
{
. . .
typedef sequence<LogItem> LogList;
}
{
public:
// Default constructor
LogList();
LogList(CORBA::ULong _max);
LogList
(
CORBA::ULong _max,
CORBA::ULong _length,
LogItem *_value,
CORBA::Boolean _relse = CORBA_FALSE
);
LogList(const LogList&);
~LogList();
CORBA::ULong length() const;
const LogItem &operator[](CORBA::ULong _index) const;
static void freebuf(LogItem *);
}; Sequence Element Mapping
Vars
Out
Member Functions
CORBA::Boolean Release);
For a bounded sequence, the length cannot be set to a value greater than the maximum.
const
TYPE
& operator[](CORBA::ULong Index) const;
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.
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 demension. 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" on page 13-7.
A var class is generated for an array. For more information, see the section "Using var Classes" on page 13-49.
An out class is generated for an array. For more information, see the section "Using out Classes" on page 13-56.
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:
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 This definition maps to C++ as follows:
// C++ class INVENT class NonExist : public CORBA::UserException 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" on page 13-7.
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" on page 13-49.
An out class is generated for an exception. For more information, see the section"Using out Classes" on page 13-56.
For a given OMG IDL exception TYPE
, the generated member functions are as follows:
{
exception NonExist
{
ID BadId;
};
};
{
. . .
{
public:
static NonExist * _narrow(CORBA::Exception_ptr);
NonExist (ID _BadId);
NonExist ();
NonExist (const NonExist &);
~NonExist ();
NonExist & operator=(const NonExist &);
void _raise ();
ID BadId;
};
}; Member Mapping
Var
Out
Member Functions
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:
Usage
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++ 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:
Mapping Rules
void release(T_ptr);
Boolean is_nil(T_ptr p);
static T_ptr _duplicate(T_ptr p);
static T_ptr _nil();
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:
Relation to the C PIDL Mapping
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:
Typedefs
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 This definition maps to C++ as follows:
// C++ class INVENT class Order : public virtual CORBA::Object class Stub_Order : public Order The generated client application stub then contains the following generated code for the stub class:
// ROUTINE NAME: INVENT::Stub_Order::modifyOrder INVENT::ItemList * INVENT::Stub_Order::modifyOrder ( Each of the arguments in an operation is mapped to the corresponding C++ type as described in Table 13-1, "Basic OMG IDL and C++ Data Types," on page 13-2 and Table 13-2, "Object, Pseudo-Object, and User-Defined OMG IDL and C++ Types," on page 13-3.
The parameter passing modes for arguments in an operation are described in Table 13-7, "Basic Argument and Result Passing," on page 13-64 and Table 13-8, "T_var Argument and Result Passing," on page 13-66.
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 // C++ 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 // C++ void f() throw(SystemException); 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++ 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++ 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++ 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 NUL), 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 This definition maps to C++ as follows:
// C++ 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 WLE 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:
Implementing Interfaces
{
interface Order
{
. . .
ItemList modifyOrder (in ItemList ModifyList);
};
};
{
. . .
{
. . .
virtual ItemList * modifyOrder (
const ItemList & ModifyList) = 0;
};
};
{
. . .
ItemList * modifyOrder (
const ItemList & ModifyList);
};
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for operation
// modifyOrder.
// (Interface : Order)
const INVENT::ItemList & ModifyList)
{
. . .
} Argument Mapping
Implementing Operations
interface A
{
exception B {};
void f() raises(B);
};
class MyA : public virtual POA_A
{
public:
void f() throw(A::B, CORBA::SystemException);
...
};
{
void f();
void g();
};
class MyA : public virtual POA_A
{
public:
void g() throw(SystemException);
private:
long x_;
};
void
MyA::f() throw(SystemException)
{
this->x_ = 3;
this->g();
} Skeleton Derivation from Object
MyImplOfA my_a; // declare impl of A
A_ptr a = &my_a; // obtain its object reference
// by C++ derived-to-base conversion
MyImplOfA my_a; // declare impl of A
A_ptr a = my_a._this(); // obtain its object reference PortableServer Functions
namespace PortableServer
{
char* ObjectId_to_string(const ObjectId&);
ObjectId* string_to_ObjectId(const char*);
} Modules
{
interface Order
{
. . .
};
};
{
. . .
class Order : public virtual CORBA::Object
{
. . .
}; // class Order
}; // class INVENT Interfaces
For example, consider the following OMG IDL definition:
// OMG IDL module INVENT This definition maps to C++ as follows:
// C++ class Order : public virtual CORBA::Object 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.
{
interface Order
{
void cancelOrder ();
};
};
class INVENT
{
. . .
class Order;
typedef Order * Order_ptr;
{
. . .
static Order_ptr _duplicate(Order_ptr obj);
static Order_ptr _narrow(CORBA::Object_ptr obj);
static Order_ptr _nil();
virtual void cancelOrder () = 0;
. . .
};
}; Generated Static Member Functions
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.
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. 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" on page 13-49.
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 This definition maps to C++ as follows:
// C++ class INVENT class Item : public virtual CORBA::Object virtual void itemInfo ( class Stub_Item : public Item void itemInfo ( The generated client application stub then contains the following generated code for the stub class:
// ROUTINE NAME: INVENT::Stub_Item::itemInfo INVENT::itemStruct * INVENT::Stub_Item::itemInfo ( ) // void INVENT::Stub_Item::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); 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, "Basic OMG IDL and C++ Data Types," on page 13-2 and Table 13-2, "Object, Pseudo-Object, and User-Defined OMG IDL and C++ Types," on page 13-3. The parameter passing modes for arguments in an operation are described in Table 13-7, "Basic Argument and Result Passing," on page 13-64 and Table 13-8, "T_var Argument and Result Passing," on page 13-66.
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:
You can obtain an object reference by using the _narrow
static member function. Operations are invoked on these classes using the arrow operator (->
).
Attributes
{
interface Order
{
. . .
attribute itemStruct itemInfo;
};
};
{
. . .
{
. . .
virtual itemStruct * itemInfo ( ) = 0;
const itemStruct & itemInfo) = 0;
};
};
{
. . .
itemStruct * itemInfo ();
const itemStruct & itemInfo);
};
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
{
. . .
}
// ROUTINE NAME: INVENT::Stub_Item::itemInfo
//
// FUNCTIONAL DESCRIPTION:
//
// Client application stub routine for attribute
// INVENT::Stub_Item::itemInfo. (Interface : Item)
const INVENT::itemStruct & itemInfo)
{
} Argument Mapping
itemStruct itemInfo (); Any Type
Handling Typed Values
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" on page 13-44 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" on page 13-44.
Type-safe insertion of arrays uses the Array_forany types described in "Arrays" on page 13-18. 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" on page 13-18.
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" on page 13-47.
The full definition of the Any class can be found in the section "Any Class Member Functions" on page 1-8
.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 WLE provides var classes for the following types:
Using var Classes
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 TYPE_var_ptr in() const; TYPE_var_ptr _retn(); The details of the member functions are as follows:
{
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& inout();
TYPE_var_ptr& out();
operator const TYPE_ptr&() const;
operator TYPE_ptr&();
operator TYPE_ptr;
};
TYPE *operator->() const;
TYPE_var_ptr& inout();
TYPE_var_ptr& out();
TYPE_var_ptr _retn();
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.
OMG IDL Data Type |
operator -> |
operator[ ] |
---|---|---|
The signatures are as shown in Table 13-4.
Sequence vars support the following additional operator[] member function:
Sequence vars
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 do not support operator->, but do support the following additional operator[] member functions to access the array elements:
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.
The String vars in the member functions described in this section and in the section "Sequence vars" on page 13-53 have a TYPE of char * . String vars support additional member functions, as follows:
String_var(const String_var & var)
String_var & operator=(const String_var & var)
char operator[] (Ulong Index) const
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" on page 13-56.
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.
OMG IDL Data Type |
operator -> |
operator[ ] |
---|---|---|
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++ // assignment from TYPE_var not allowed 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 non-null
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++ 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++ 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++ Sequence outs support the following additional operator[]
member function:
Using out Classes
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_;
void operator=(const TYPE_var&):
};
TYPE_var t = ...;
my_out = new TYPE(t.in()); // heap-allocate a copy
TYPE_var t = ...;
my_out = t._retn(); // t yields ownership, no copy Object Reference out Parameter
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
Array outs do not support operator-> , but do support the following additional operator[] member functions to access the array elements:
const TYPE_slice & operator[](CORBA::ULong Index) const;
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 WLE 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, "Basic Argument and Result Passing," on page 13-64 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, "T_var Argument and Result Passing," on page 13-66 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.
Data Type |
In |
Inout |
Out |
Return |
---|---|---|---|---|
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.
Data Type |
In |
Inout |
Out |
Return |
---|---|---|---|---|
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.
Type |
Inout Param |
Out Param |
Return Result |
---|---|---|---|
|
Copyright © 1999 BEA Systems, Inc. All rights reserved.
|