[Up: C++ mapping]
[Previous: Arrays] [Next: Interface inheritance]

# Unions

Unions and structs in the CORBA-IDL allow the definition of constructed data types. Each of them is defined through a set of members. Is a struct used as an input parameter of an operation, all of its members will be transmitted, whereas for a union at most one of its members will actually be transmitted. The purpose of an IDL-union is similar to that of a C-union: reduction of memory usage. This is especially important in a middleware platform where less memory space for a data type also means less data to transfer over the network. One must carefully consider, when structs or unions should be used.

A special problem arises with unions when they are being used as parameters of operation invocations: how does the receiving object know which of the different members holds a valid value? In order to make a distinction for this case, the IDL-union is a combination of a C-union and a C-switch statement. Each member is clearly tagged with a value of a given discriminator type (see also `mico/test/idl/21`):

      // IDL
      typedef octet Bytes[64];
      struct S { long len; };
      interface A;

      union U switch (long) {
        case 1: long x;
        case 2: Bytes y;
        case 3: string z;
        case 4:
        case 5: S w;
        default: A obj;
      };

In the union `U` as shown above, `long` is the discriminator type. The values following the case label must belong to this discriminator type. All integer types and enums are valid discriminator types. Unions map to C++ classes with access functions for the union members and discriminant. The default union constructor performs no application-visible initialization of the union. It does not initialize the discriminator, nor does it initialize any union members to a state useful to an application. It is therefore an error for an application to access the union before setting it. The copy constructor and assignment operator both perform a deep-copy of their parameters, with the assignment operator releasing old storage if necessary. The destructor releases all storage owned by the union. The following example helps illustrate the mapping for union types for the union `U` as shown above:

      // Generated C++ code
      typedef CORBA::Octet Bytes[64];
      typedef CORBA::Octet Bytes_slice;
      template<...> Bytes_forany;
      struct S { CORBA::Long len; };
      typedef ... A_ptr;

      class U {
        public:
          //...
          void _d( CORBA::Long );
          CORBA::Long _d() const;

          void x( CORBA::Long );
          CORBA::Long x() const;

          void y( Bytes );
          Bytes_slice *y() const;

          void z( char* );             // free old storage, no copy
          void z( const char* );       // free old storage, copy
          void z( const String_var& ); // free old storage, copy
          const char *z() const;

          void w( const S & ); // deep copy
          const S &w() const;  // read-only access
          S &w();              // read-write access

          void obj( A_ptr ); // release old objref, duplicate
          A_ptr obj() const; // no duplicate
      };

The union discriminant access functions have the name `_d` to both be brief and avoid name conflicts with the members. The `_d` discriminator modifier function can only be used to set the discriminant to a value within the same union member. In addition to the `_d` accessors, a union with an implicit default member provides a `_default()` member function that sets the discriminant to a legal default value. A union has an implicit default member if it does not have a default case and not all permissible values of the union discriminant are listed.

Setting the union value through an access function automatically sets the discriminant and may release the storage associated with the previous value. Attempting to get a value through an access function that does not match the current discriminant results in undefined behavior. If an access function for a union member with multiple legal discriminant values is used to set the value of the discriminant, the union implementation will choose the value of the first case label in the union (e.g. value for the member `w` of union `U`), although it could be any other value for that member as well.

The restrictions for using the `_d` discriminator modifier function are shown by the following examples, based on the definition of the union `U` shown above:

      // C++
      S s = ...;
      A_ptr a = ...;
      U u;

      u.w( s );   // member w selected, discrimintator == 4
      u._d( 4 );  // OK, member w selected
      u._d( 5 );  // OK, member w selected
      u._d( 1 );  // error, different member selected
      u.obj( a ); // member obj selected
      u._d( 7 );  // OK, member obj selected
      u._d( 1 );  // error, different member selected

As shown here, the `_d` modifier function cannot be used to implicitly switch between different union members. The following shows an example of how the `_default()` member function is used:

      // IDL
      union Z switch(boolean) {
        case TRUE: short s;
      };

      // C++
      Z z;
      z._default();  // implicit default member selected
      CORBA::Boolean disc = z._d(); // disc == FALSE
      U u;           // union U from previous example
      u._default();  // error, no _default() provided

For union `Z`, calling the `_default()` member function causes the union's value to be composed solely of the discriminator value of `FALSE`, since there is no explicit default member. For union `U`, calling `_default()` causes a compilation error because `U` has an explicitly declared default case and thus no `_default()` member function. A `_default()` member function is only generated for unions with implicit default members.

For an array union member, the accessor returns a pointer to the array slice, where the slice is an array with all dimensions of the original except the first (see section 5.3 for a discussion on array slices). The array slice return type allows for read-write access for array members via regular subscript operators. For members of an anonymous array type, supporting typedefs for the array are generated directly into the union. For example:

      // IDL
      union U switch (long) {
        case 1: long array[ 3 ][ 4 ];
      };

      // Generated C++ code
      class U {
        public:
        // ...
        typedef long _array_slice[ 4 ];
        void array( long arg[ 3 ][ 4 ] );
        _array_slice* array();
      };

The name of the supporting array slice typedef is created by prepending an underscore and appending `_slice` to the union member name. In the example above, the array member named `_array` results in an array slice typedef called `_array_slice` nested in the union class.

[Previous: Arrays] [Next: Interface inheritance]
[Up: C++ mapping]