[Up: C++ mapping]
[Previous: Using strings] [Next: Arrays]

Subsections

Untyped values

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


Table 5.1: Any insertion and extraction operators
IDL type <<= nc. <<= >>= nc. >>=
base type +   +  
enum +   +  
any + + + +
fixed + + + +
string + +   +
wstring + +   +
sequence + + + +
array + +   +
struct + + + +
union + + + +
interface + +   +
pseudo objs + +   +
valuetype + +   +


Unknown Constructed Types

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.

Subtyping

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 $T_1$ is a subtype of type $T_2$ if you could pass $T_1$ as an input parameter where a $T_2$ is expected. This means for basic types such as long: a basic type $T_1$ is a subtype of a basic type $T_2$ iff the set of possible values of $T_1$ is a subset of the set of possible values of $T_2$. 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.

Figure 5.1: Subtype relations between basic CORBA types.
\begin{figure}
\begin{center}
\ \psfig{file=pics/subtype.eps,width=7cm}\end{center}\end{figure}

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 $T_1$ is a subtype of a struct type $T_2$ iff the elements of $T_2$ are supertypes of the first elements of $T_1$. 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]

Frank Pilhofer
2001-09-28