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

Subsections

- Unknown Constructed Types
- Subtyping

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

Figure 5.1: Subtype relations between basic CORBA types.

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]