[Up: C++ mapping]
[Previous: Using strings] [Next: Arrays]
The handling of untyped values is one of CORBAs strengths. The pre-defined C++ class Any in the namespace CORBA provides this support. An instance of class Any represents a value of an arbitrary IDL-type. For each type, the class Any defines the overloaded operators >>= and <<=. These two operators are responsible for the insertion and extraction of the data values. The following code fragment demonstrates the usage of these operators:
// C++ CORBA::Any a; // Insertion into any a <<= (CORBA::ULong) 10; // Extraction from any CORBA::ULong l; a >>= l;
At the end of this example the variable l should have the value 10. The library of MICO provides overloaded definitions of these operators for all basic data types. Some of these data types are ambiguous in the sense that they collide with other basic data types. This is true for the IDL-types boolean, octet, char and string. For each of these IDL-types, CORBA prescribes a pair of supporting functions which help to disambiguate the type clashes. For the type boolean for example the usage of these supporting function is:
CORBA::Any a; // Insertion into any a <<= CORBA::Any::from_boolean( TRUE ); // Extraction from any CORBA::Boolean b; a >>= CORBA::Any::to_boolean( b );
The usage of the other supporting functions for octet, char and string is equivalent. For bounded strings the supporting functions from_string and to_string accept an additional long-parameter which reflects the bound.
For each type defined in an IDL specification, the IDL-compiler generates an overloaded version of the operators >>= and <<=. For example given the following IDL specification:
// IDL struct S1 { long x; char c; }; struct S2 { string str; };
The MICO IDL-compiler will automatically generate appropriate definitions of >>= and <<= for the IDL types S1 and S2. The following code fragment demonstrates the usage of these operators:
1: void show_any( const CORBA::Any& a ) 2: { 3: S1 s1; 4: S2 s2; 5: 6: if( a >>= s1 ) { 7: cout << "Found struct S1" << endl; 8: cout << s1.x << endl; 9: cout << s1.c << endl; 10: } 11: if( a >>= s2 ) { 12: cout << "Found struct S2" << endl; 13: cout << s2.str << endl; 14: } 15: } 16: 17: int main( int argc, char *argv[] ) 18: { 19: //... 20: CORBA::Any a; 21: 22: S2 s2; 23: s2.str = (const char *) "Hello"; 24: a <<= s2; 25: show_any( a ); 26: 27: S1 s1; 28: s1.x = 42; 29: s1.c = 'C'; 30: a <<= s1; 31: show_any( a ); 32: }
The main program first initializes an instance of a S2 (lines 22-24) and then calls the function show_any. Function show_any tries to extract the value contained in the any. This example also demonstrates how to tell whether the extraction was successful or not. The operator >>= returns true, iff the type of the value contained in the any matches with the type of the variable of the right side of >>=. If the any should contain something else than S1 or S2, then show_any will fall through both if-statements in lines 6 and 11. The complete sources for the above example can be found in mico/test/idl/14.
For some IDL types two different >>=
and <<=
operators
are provided: a copying and a non-copying version. The copying
version of the <<=
operator takes a reference to the IDL type
and inserts a copy of it into the Any
. The non-copying version
takes a pointer to the IDL type and moves it into the Any
without making a copy. The user must not access the inserted value
afterwards. The copying version of the >>=
operator takes a
reference to the IDL type and copies the value of the Any
into
it. The non-copying version takes a reference to a pointer to the IDL
type and points it to the value in the Any
. The user
must not free the returned value. Here are some examples:
// IDL struct foo { long l; short s; }; // C++ CORBA::Any a; // copying <<= foo f; a <<= f; // non-copying <<= foo *f = new foo; a <<= f; // do not touch 'f' here ... // copying >>= foo f; a >>= f; // non-copying >>= foo *f; a >>= f; // do not free 'f' // changing 'a' invalidates 'f'
Table 5.1 gives an overview of the operators provided for each IDL type (nc. means non-copying).
MICO's Any
implementation offers an extended interface for
typesafe insertion and extraction of constructed types that were not
known at compile time. This interface is also used by the <<=
and
>>=
operators generated by the IDL compiler for constructed
types. Lets look at the generated operators for a simple structure:
1: // IDL 2: struct foo { 3: long l; 4: short s; 5: }; 6: 7: // C++ 8: void operator<<= ( CORBA::Any &a, const foo &s ) 9: { 10: a.type( _tc_foo ); 11: a.struct_put_begin(); 12: a <<= s.l; 13: a <<= s.s; 14: a.struct_put_end(); 15: } 16: 17: CORBA::Boolean operator>>=( const CORBA::Any &a, foo &s ) 18: { 19: return a.struct_get_begin() && 20: (a >>= s.l) && 21: (a >>= s.s) && 22: a.struct_get_end(); 23: }
The <<=
operator tells the Any
the TypeCode
(_tc_foo
) of the to be inserted structure in line 10. Those
_tc_*
constants are generated by the IDL compiler as well. If you
want to insert a constructed type that was not known at compile time you
have to get the TypeCode
from somewhere else (e.g., from the
interface repository) or you have to create one using the
create_*_tc()
ORB methods.
After telling the Any
the TypeCode
the <<=
operator
opens a structure in line 11, shifts in the elements of the struct in lines
12-13 and closes the struct in line 14. While doing so the Any
checks the correctness of the inserted items using the TypeCode
. If
it detects an error (e.g., the TypeCode
says the first element of the
struct is a short and you insert a float) the corresponding method or
<<=
operator will return FALSE. If the structure contained another
constructed type you had to make nested calls to struct_put_begin()
and struct_put_end()
or the corresponding methods for unions,
exceptions, arrays, or sequences.
The >>=
operator in lines 17-23 has the same structure as
the <<=
operator but uses >>=
operators to extract the struct
elements and struct_get_begin()
and struct_get_end()
to open
and close the structure. There is no need to specify a TypeCode
before
extraction because the Any
knows it already.
Another feature of MICO's Any
implementation is its subtyping
support. The extraction operators of type Any
implement the
subtyping rules for recursive types as prescribed by the
Reference Model for Open Distributed Processing (RM-ODP), see
[1,2,3,4] for details. The idea behind
subtyping is the following: Imagine you want to call a CORBA method
void bar (in long x);
but want to pass a short
as an argument instead of the required
long
. This should work in theory since each possible
short
value is also a long
value which means
short
is a subtype of long
. More generally speaking a
type is a subtype of type
if you could pass
as an
input parameter where a
is expected. This means for basic types
such as
long
: a basic type is a subtype of a basic type
iff the set of possible values of
is a subset of the set
of possible values of
. Figure 5.1 shows the
subtype relations between CORBA's basic data types. In C++ the
compiler can automatically convert types along a chain of arrows, but
in a distributed CORBA application this can't be done by the compiler
alone because binding between client and server is performed at
runtime using a trader or a naming service. That is the subtype
checking must be done at runtime as well.
In MICO the Any
type performs subtype checking at runtime. For
example:
// C++ CORBA::Any a; a <<= (CORBA::Short) 42; ... CORBA::Double d; a >>= d;
will work because short
is a subtype of double
according to
figure 5.1 but:
// C++ CORBA::Any a; a <<= (CORBA::Long) 42; ... CORBA::ULong d; a >>= d;
will fail because long is not a subtype of unsigned
long. There is a special subtyping rule for structured types: A
struct type is a subtype of a struct type
iff the elements
of
are supertypes of the first elements of
. struct
S1 is for example a subtype of struct S2:
struct S1 { short s; long l; }; struct S2 { long s; };
That is you can put a struct S1
into an Any
and unpack it
as a struct S2
later:
// C++ CORBA::Any a; S1 s1 = { 10, 20 }; a <<= s1; ... S2 s2; a >>= s2;
There are similar rules for the other constructed types.
[Previous: Using strings] [Next: Arrays]
[Up: C++ mapping]