In CORBA specification 2.2, a new facility- DynAny is introduced. Previously, it is not possible to insert or extract constructed and other complex types from an any without using the stub code generated by an idl compiler for these types. This makes it impossible to write generic servers (bridges, event channels supporting filtering etc) because these servers can not have static knowledge of all the possible data types that they have to handle.
To fill this gap, the DynAny facility is defined to enable traversal of the data value associated with an any at runtime and extraction of its constituents. This facility also enables the construction of an any at runtime, without having static knowledge of its types.
This chapter explains how DynAny may be used. For completeness, you should also read the DynAny specification defined in Chapter 7 of the CORBA specification 2.2. Where possible, the implementation in omniORB2 adheres closely to the specification. However, there are areas in the specification that are ambiguous or lacking in details. A number of these issues are currently opened with the ORB revision task force. Until the issues are resolved, it is possible that a different implementation may choose to intepret the specification differently. This chapter provides clarifications to the specification, explains the interpretation used and offers some advice and warnings on potential portability problems.
Notice that the DynAny interface has been changed in CORBA 2.3, particularly with the addition of the support for the IDL type valuetype. Future releases of omniORB will be updated to implement the interface as defined in CORBA 2.3.
// namespace CORBA class ORB { public: ... class InconsistentTypeCode : public UserException { ... }; DynAny_ptr create_dyn_any(const Any& value); DynAny_ptr create_basic_dyn_any(TypeCode_ptr tc); DynStruct_ptr create_dyn_struct(TypeCode_ptr tc); DynSequence_ptr create_dyn_sequence(TypeCode_ptr tc); DynArray_ptr create_dyn_array(TypeCode_ptr tc); DynUnion_ptr create_dyn_union(TypeCode_ptr tc); DynEnum_ptr create_dyn_enum(TypeCode_ptr tc); }; typedef DynAny* DynAny_ptr; class DynAny_var { ... }; class DynAny { public: class Invalid : public UserException { ... }; class InvalidValue : public UserException { ... }; class TypeMismatch : public UserException { ... }; class InvalidSeq : public UserException { ... }; typedef _CORBA_Unbounded_Sequence__Octet OctetSeq; TypeCode_ptr type() const; void assign(DynAny_ptr dyn_any) throw(Invalid,SystemException); void from_any(const Any& value) throw(Invalid,SystemException); Any* to_any() throw(Invalid,SystemException); void destroy(); DynAny_ptr copy(); DynAny_ptr current_component(); Boolean next(); Boolean seek(Long index); void rewind(); void insert_boolean(Boolean value) throw(InvalidValue,SystemException); void insert_octet(Octet value) throw(InvalidValue,SystemException); void insert_char(Char value) throw(InvalidValue,SystemException); void insert_short(Short value) throw(InvalidValue,SystemException); void insert_ushort(UShort value) throw(InvalidValue,SystemException); void insert_long(Long value) throw(InvalidValue,SystemException); void insert_ulong(ULong value) throw(InvalidValue,SystemException); void insert_float(Float value) throw(InvalidValue,SystemException); void insert_double(Double value) throw(InvalidValue,SystemException); void insert_string(const char* value) throw(InvalidValue,SystemException); void insert_reference(Object_ptr v) throw(InvalidValue,SystemException); void insert_typecode(TypeCode_ptr v) throw(InvalidValue,SystemException); void insert_any(const Any& value) throw(InvalidValue,SystemException); Boolean get_boolean() throw(TypeMismatch,SystemException); Octet get_octet() throw(TypeMismatch,SystemException); Char get_char() throw(TypeMismatch,SystemException); Short get_short() throw(TypeMismatch,SystemException); UShort get_ushort() throw(TypeMismatch,SystemException); Long get_long() throw(TypeMismatch,SystemException); ULong get_ulong() throw(TypeMismatch,SystemException); Float get_float() throw(TypeMismatch,SystemException); Double get_double() throw(TypeMismatch,SystemException); char* get_string() throw(TypeMismatch,SystemException); Object_ptr get_reference() throw(TypeMismatch,SystemException); TypeCode_ptr get_typecode() throw(TypeMismatch,SystemException); Any* get_any() throw(TypeMismatch,SystemException); static DynAny_ptr _duplicate(DynAny_ptr); static DynAny_ptr _narrow(DynAny_ptr); static DynAny_ptr _nil(); }; // DynFixed is not supported. typedef DynEnum* DynEnum_ptr; class DynEnum_var { ... }; class DynEnum : public DynAny { public: char* value_as_string(); void value_as_string(const char* value); ULong value_as_ulong(); void value_as_ulong(ULong value); static DynEnum_ptr _duplicate(DynEnum_ptr); static DynEnum_ptr _narrow(DynAny_ptr); static DynEnum_ptr _nil(); }; typedef char* FieldName; typedef String_var FieldName_var; struct NameValuePair { String_member id; Any value; }; typedef _CORBA_ConstrType_Variable_Var<NameValuePair> NameValuePair_var; typedef _CORBA_Unbounded_Sequence<NameValuePair > NameValuePairSeq; typedef DynStruct* DynStruct_ptr; class DynStruct_var { ... }; class DynStruct : public DynAny { public: char* current_member_name(); TCKind current_member_kind(); NameValuePairSeq* get_members(); void set_members(const NameValuePairSeq& NVSeqVal) throw(InvalidSeq,SystemException); static DynStruct_ptr _duplicate(DynStruct_ptr); static DynStruct_ptr _narrow(DynAny_ptr); static DynStruct_ptr _nil(); }; typedef DynUnion* DynUnion_ptr; class DynUnion_var { ... }; class DynUnion : public DynAny { public: Boolean set_as_default(); void set_as_default(Boolean value); DynAny_ptr discriminator(); TCKind discriminator_kind(); DynAny_ptr member(); char* member_name(); void member_name(const char* value); TCKind member_kind(); static DynUnion_ptr _duplicate(DynUnion_ptr); static DynUnion_ptr _narrow(DynAny_ptr); static DynUnion_ptr _nil(); }; typedef _CORBA_Unbounded_Sequence<Any > AnySeq; typedef DynSequence* DynSequence_ptr; class DynSequence_var { ... }; class DynSequence : public DynAny { public: ULong length(); void length (ULong value); AnySeq* get_elements(); void set_elements(const AnySeq& value) throw(InvalidValue,SystemException); static DynSequence_ptr _duplicate(DynSequence_ptr); static DynSequence_ptr _narrow(DynAny_ptr); static DynSequence_ptr _nil(); }; typedef DynArray* DynArray_ptr; class DynArray_var { ... }; class DynArray : public DynAny { public: AnySeq* get_elements(); void set_elements(const AnySeq& value) throw(InvalidValue,SystemException); static DynArray_ptr _duplicate(DynArray_ptr); static DynArray_ptr _narrow(DynAny_ptr); static DynArray_ptr _nil(); };
If an any contains a value of one of the basic data types, its value can be extracted using the pre-defined operators in the Any interface. When the value is a struct or other non-basic types, one can use the DynAny interface to extract its constituent values.
In this section, we use a struct as an example to illustrate how the DynAny interface can be used.
The example struct is as follows:
// IDL struct exampleStruct1 { string s; double d; long l; };
To create a DynAny from an Any value, one uses the create_dyn_any method:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of type exampleStruct1. CORBA::DynAny_var dv = orb->create_dyn_any(v);
Like CORBA object and pseudo object references, a DynAny_ptr can be managed by a _var type ( DynAny_var) which will release the DynAny_ptr automatically when the variable goes out of scope.
Once the DynAny object is created, we can use the DynAny interface to extract the individual components in exampleStruct1. The DynAny interface provides a number of functions to extract and insert component values. These functions are defined to operate on the component identified by the current component pointer.
A current component pointer is an internal state of a DynAny object. When a DynAny object is created, the pointer is initialised to point to the first component of the any value.
The pointer can be advanced to the next component with the next() operation. The function returns FALSE (0) if there are no more components. Otherwise it returns TRUE (1). When the any value in the DynAny object contains only one component, the next() operation always returns FALSE(0).
Another way of adjusting the pointer is the seek() operation. The function returns FALSE (0) if there is no component at the specified index. Otherwise it returns TRUE (1). The index value of the first component is zero. Therefore, a seek(0) call rewinds the pointer to the first component, this is also equivalent to a call to the rewind() operation.
For completeness, we should also mention here the current_component() operation. This operation causes the DynAny object to return a reference to another DynAny object that can be used to access the current component. It is possible that the current component pointer is not pointing to a valid component, for instance, the next() operation has been invoked and there is no more component. Under this circumstance, the current_component() operation returns a nil DynAny object reference. For components which are just basic data types, calling current_component() is an overkill because we can just use the basic type extraction and insertion functions directly.
In our example, the component values can be extracted as follows:
CORBA::String_var s = dv->get_string(); CORBA::Double d = dv->get_double(); CORBA::Long l = dv->get_long();
Each get basic type operation has the side-effect of advancing the current component pointer. For instance:
CORBA::String_var s = dv->get_string();
is equivalent to:
CORBA::DynAny_var temp = dv->current_component(); CORBA::String_var s = temp->get_string(); dv->next();
The get operations ensure that the current component is of the same type as requested. Otherwise, the object throws a TypeMismatch exception. If the current component pointer is invalid when a get operation is called, the object also throws a TypeMismatch exception.
To repeatedly access the components, one can use the rewind() or seek() operation to manipulate the current component pointer. For instance, to access the d member in exampleStruct1 directly:
dv->seek(1); // position current component to member d. CORBA::Double d = dv->get_double();
When a component is not one of the basic data types, it is not possible to extract its value using the get operations. Instead, a DynAny object has to be created from which the component is accessed.
Consider this example:
// IDL struct exampleStruct2 { string m1; exampleStruct1 m2; };
In order to extract the data members within m2 (of type exampleStruct1), we use current_component() as follows:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of type exampleStruct2. CORBA::DynAny_var dv = orb->create_dyn_any(v); CORBA::String_var m1 = dv->get_string(); // extract member m1 CORBA::DynAny_var dm = dv->current_component(); // DynAny reference to m2 CORBA::String_var s = dm->get_string(); // m2.s CORBA::Double d = dm->get_double(); // m2.d CORBA::Long l = dm->get_long(); // m2.l
Now we finish off this example with a description on destroying DynAny objects. There are two points to remember:
In the example, the DynAny object can be destroyed as follows:
// C++ ... CORBA::DynAny_var dv = orb->create_dyn_any(v); ... dv->destroy(); // From now on, one should not invoke any operation in dv. Otherwise the // behavior is undefined.
Using the DynAny interface, one can create an Any value from scratch. In this example, we are going to create an Any containing the value of the exampleStruct1 type.
Firstly, we have to create a DynAny to store the value using one of the create_dyn functions. Because exampleStruct1 is a struct type, we use the create_dyn_struct() operation.
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. // create the TypeCode for exampleStruct. StructMemberSeq tc_members; tc_members.length(3); tc_members[0].name = (const char*)"s"; tc_members[0].type = CORBA::TypeCode::_duplicate(CORBA::_tc_string); tc_members[0].type_def = CORBA::IDLType::_nil(); tc_members[1].name = (const char*)"d"; tc_members[1].type = CORBA::TypeCode::_duplicate(CORBA::_tc_double); tc_members[1].type_def = CORBA::IDLType::_nil(); tc_members[2].name = (const char*)"l"; tc_members[2].type = CORBA::TypeCode::_duplicate(CORBA::_tc_long); tc_members[2].type_def = CORBA::IDLType::_nil(); CORBA::TypeCode_var tc = orb->create_struct_tc("IDL:exampleStruct1:1.0", "exampleStruct1", tc_members); // create the DynAny object to represent the any value CORBA::DynAny_var dv = orb->create_dyn_struct(tc);
Once the DynAny object is created, we can use the DynAny interface to insert the components. The DynAny interface provides a number of insert operations to insert basic types into the any value. In our example, the component values can be inserted as follows:
CORBA::String_var s = (const char*)"Hello"; CORBA::Double d = 3.1416; CORBA::Long l = 1; dv->insert_string(s); dv->insert_double(d); dv->insert_long(l);
Each insert basic type operation has the side-effect of advancing the current component pointer. For instance:
dv->insert_string(s);
is equivalent to:
CORBA::DynAny_var temp = dv->current_component(); temp->insert_string(s); dv->next();
The insert operations ensure that the current component is of the same type as the inserted value. Otherwise, the object throws a InvalidValue exception. If the current component pointer is invalid when an insert operation is called, the object also throws a InvalidValue exception.
Sometimes, one may just want to modify one component in an Any value. For instance, one may just want to change the value of the double member in exampleStruct1. This can be done as follows:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of type exampleStruct1. CORBA::Double d = 6.28; CORBA::DynAny_var dv = orb->create_dyn_any(v); dv->seek(1); dv->insert_double(d); // Change the value of the member d.
Finally, the any value can be obtained from the DynAny object using the to_any() operation:
CORBA::Any_var v = dv->to_any(); // Obtain the any value.
When a component is not one of the basic data types, it is not possible to insert its value using the insert operations. Instead, a DynAny object has to be created through which the component can be inserted.
In our example, one can insert component values into exampleStruct2 as follows:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. CORBA::TypeCode_var tc; // create the TypeCode for exampleStruct2. ... // create the DynAny object to represent the any value CORBA::DynAny_var dv = orb->create_dyn_struct(tc); CORBA::String_var m1 = (const char*)"Greetings"; CORBA::String_var m2s = (const char*)"Hello"; CORBA::Double m2d = 3.1416; CORBA::Long m2l = 1; dv->insert_string(m1); // insert member m1 CORBA::DynAny_var dm = dv->current_component(); // DynAny reference to m2 dm->insert_string(m2s); // insert member m2.s dm->insert_double(m2d); // insert member m2.d dm->insert_long(m2l); // insert member m2.l CORBA::Any_var v = dv->to_any(); // obtain the any value dv->destroy(); // destroy the DynAny object. // No operation should be invoked on dv // from this point on except CORBA::release.
In addition to the DynAny interface, a number of derived interfaces are defined. These interfaces are specialisation of the DynAny interface to facilitate the handling of any values containing non-basic types: struct, sequence, array, enum and union. The next few sections will provide more details on these interfaces.
When a DynAny object is created through the create_dyn_any() operation and the any value contains a struct type, a DynStruct object is created. The DynAny reference returned can be narrowed to a DynStruct reference using the CORBA::DynStruct::_narrow() operation.
In the previous example, the components are extracted using the get operations. Alternatively, the DynStruct interface provides an addition operation ( get_members()) to return all the components in a single call. The returned value is a sequence of name value pair. The member name is given in the name field and its value is returned as an Any value. For example, an alternative way to extract the components in the previous example is as follows:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of type exampleStruct1. CORBA::DynAny_var dv = orb->create_dyn_any(v); CORBA::DynStruct_var ds = CORBA::DynStruct::_narrow(dv); CORBA::NameValuePairSeq* sq = ds->get_members(); char* s; CORBA::Double d; CORBA::Long l; (*sq)[0].value >>= s; // 1st element contains member s (*sq)[1].value >>= d; // 2nd element contains member d (*sq)[2].value >>= l; // 3rd element contains member l
Similarly, the DynStruct interface provides an addition operation ( set_members()) to insert all the components in a single call. The following is an alternative way to insert the components of the type exampleStruct1 into an Any value:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. CORBA::TypeCode_var tc; // create the TypeCode for exampleStruct1. ... // create the DynAny object to represent the any value CORBA::DynAny_var dv = orb->create_dyn_struct(tc); CORBA::String_var s = (const char*)"Hello"; CORBA::Double d = 3.1416; CORBA::Long l = 1; CORBA::NameValuePairSeq sq; sq.length(3); sq[0].id = (const char*)"s"; sq[0].value <<= CORBA::Any::from_string(s,0); // 1st element contains member s sq[1].id = (const char*)"d"; sq[1].value <<= d; // 2nd element contains member d sq[2].id = (const char*)"l"; sq[2].value <<= l; // 3rd element contains member l dv->set_members(sq);
Notice that the name-value pairs in the argument to set_members() must match the members of the struct exactly or the object would throw the InvalidSeq exception.
In addition to the current_component() operation, the DynStruct interface provides two operations: current_member_name() and current_member_kind(), to return information about the current component.
Like struct values, sequence values can be traversed using the operations introduced in section 10.2. The first sequence element can be accessed as the first DynAny component, the second sequence element as the second DynAny component and so on.
To extract component values from an Any containing a sequence, the length of the sequence can be obtained using the get length operation in the DynSequence interface. Here is an example to extract the components of a sequence of long:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of a sequence of long CORBA::DynAny_var dv = orb->create_dyn_any(v); CORBA::DynSequence_var ds = CORBA::DynSequence::_narrow(dv); CORBA::ULong len = ds->length(); // extract the length of the sequence CORBA::ULong index; for (index = 0; index < len; index++) { CORBA::Long v = ds->get_long(); cerr << "[" << index << "] = " << v << endl; }
Conversely, the set length operation is provided to set the length of the sequence. Here is an example to insert the components of a sequence of long:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. CORBA::TypeCode_var tc; // create the TypeCode for a sequence of long. ... // create the DynAny object to represent the any value CORBA::DynSequence_var ds = orb->create_dyn_sequence(tc); CORBA::ULong len = 3; ds->length(len); // set the length of the sequence CORBA::ULong index; for (index = 0; index < len; index++) { ds->insert_long(index); // insert a sequence element }
Similar to the DynStruct interface, the get_elements() operation is provided to return all the sequence elements and the set_elements() operation is provided to insert all the sequence elements.
Array values are handled by the DynArray interface. The DynArray interface is the same as the DynSequence interface except that the former does not provide the set length and get length operations.
Enum values are handled by the DynEnum interface. A DynEnum object contains a single component which is the enum value. This value cannot be extracted or inserted using the get and insert operations of the DynAny interface. Instead, two pairs of operations are provided to handle this value.
The value_as_string operations allow the enum value to be extracted or inserted as a string. The value_as_ulong operations allow the enum value to be extracted or inserted as an unsigned long.
Union values are handled by the DynUnion interface. Unfortunately, the CORBA 2.2 specification does not define the DynUnion interface in sufficent details to nail down its intended usage. In this section, we try to fill in the gaps and describe a sensible way to use the DynUnion interface. Where necessary, the semantics of the operations is clarified. It is possible that the behavior of this interface in another ORB is different from this implmentation. Where appropriate, we give warnings on usage that might cause problems with portability.
In relation to the current component pointer (10.2.1.1), a DynUnion object contains two components. The first component (with the index value equals 0) is the discriminator value, the second one is the member value. Therefore, one can use the seek() and current_component() operations to obtain a reference to the DynAny objects that handle the two components. However, it is better to use the operations defined in the DynUnion interface to manipulate these components as the semantics of the operations is easier to understand.
Before we continue, it is important to understand that unions can be classified into the following categories:
Of the three categories, the implicit default union is interesting because by definition if the discriminator value is not equal to any of the branch labels, the union has no member. That is, the union value consists solely of the discriminator value.
Consider a union of the following type:
// IDL union exampleUnion1 switch(boolean) { case TRUE: long l; default: double d; };
The most straightforward way to extract the member value is as follows:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. Any v; ... // Initialise v to contain a value of type exampleUnion1. CORBA::DynAny_var dv = orb->create_dyn_any(v); CORBA::DynUnion_var du = CORBA::DynUnion::_narrow(dv); CORBA::String_var di = du->member_name(); CORBA::DynAny_var dm = du->member(); if (strcmp((const char*)di,"l") == 0) { // branch label is TRUE CORBA::Long v = dm->get_long(); cerr << "l = " << v << endl; } if (strcmp((const char*)di,"d") == 0) { // Is default branch CORBA::Double v = dm->get_double(); cerr << "d = " << v << endl; }
In the example, the operation member_name() is used to determine which branch the union has been instantiated. The operation member() is used to obtain a reference to the DynAny object that handles the member.
Alternatively, the branch can be determined by reading the discriminator value:
// C++ CORBA::DynAny_var di = du->discriminator(); CORBA::DynAny_var dm = du->member(); CORBA::Boolean di_v = di->get_boolean(); switch (di_v) { case 1: CORBA::Long v = dm->get_long(); cerr << "l = " << v << endl; break; default: CORBA::Double v = dm->get_double(); cerr << "d = " << v << endl; }
The operation discriminator() is used to obtain the value of the discriminator.
Finally, the third way to determine the branch is to test if the default is selected:
// C++ switch (dv->set_as_default()) { case 1: CORBA::Double v = dm->get_double(); cerr << "d = " << v << endl; break; default: CORBA::Long v = dm->get_long(); cerr << "l = " << v << endl; }
The operation set_as_default() returns TRUE (1) if the discriminator has been assigned a valid default value.
Consider a union of the following type:
// IDL union exampleUnion2 switch(long) { case 1: long l; case 2: double d; };
This example is similar to the previous one but there is no default branch. The description above also applies to this example. However, the discriminator may be set to neither 1 nor 2. Under this condition, the implicit default is selected and the union value contains the discriminator only!
When the discriminator contains an implicit default value, one might ask what is the value returned by the member_name() and member() operation. Since there is no member in the union value, omniORB2 returns a null string and a nil DynAny reference respectively. This behavior is not specified in the CORBA 2.2 specification. To ensure that your application is portable, it is best to avoid calling these operations when the DynUnion object might contain an implicit default value.
This is the last union category. For instance:
// IDL union exampleUnion3 switch(boolean) { case TRUE: long l; case FALSE: double d; };
In this example, all the possible values of the discriminator are used as union labels. There is no default branch. The only difference between this category and the explicit default union is that the set_as_default() operation always returns FALSE (0).
Writing into a union involves selecting the union branch with the appropriate discriminator value and then writing the member value. There are three ways to set the discriminator value:
The following example shows the three ways of writing into a union:
// C++ CORBA::ORB_ptr orb; // orb initialised by CORBA::ORB_init. CORBA::TypeCode_var tc; // create the TypeCode for exampleUnion1. ... // create the DynAny object to represent the any value CORBA::DynUnion_var dv = orb->create_dyn_union(tc); CORBA::Any_var v; DynAny_ptr dm; // Use member_name to select the union branch dv->member_name("l"); dm = dv->member(); dm->insert_long(10); v = dv->to_any(); // transfer to an Any CORBA::release(dm); // Setting the discriminator value to select the union branch CORBA::DynAny_var di = dv->discriminator(); di->insert_boolean(1); // set discriminator to label TRUE dm = dv->member(); dm->insert_long(20); v = dv->to_any(); // transfer to an Any CORBA::release(dm); // Use set_as_default to select the default union branch dv->set_as_default(1); dm = dv->member(); dm->insert_double(3.14); v = dv->to_any(); // transfer to an Any CORBA::release(dm); dv->destroy();
To ensure portability, it is best to avoid using the DynUnion interface and not to rely on the ORB to behave as omniORB2 does under these ambiguous conditions.
Like any CORBA object and psuedo object references, a DynAny reference can be duplicated using the _duplicate() operations. When an application has obtained multiple DynAny references to the same DynAny object, it should be noted that a change made to the object by invoking on one reference is also visible through the other references. In particular, if a call through one reference has caused the current component pointer to be changed, subsequent calls through other references will operate on the new current component pointer.
The following is a short summary of the other operations in the DynAny interface which have not been covered in previous sections: