Orbix Programmer's Guide C++ Edition


Chapter 12

The Any Data Type

This chapter explains the IDL type any and the corresponding C++ class CORBA::Any, using an example. IDL type any indicates that a value of an arbitrary type can be passed as a parameter or a return value.

This chapter discusses different means of constructing and interpreting an any. It first discusses the use of operator<<= (left-shift assign operator) and operator >>= (right-shift assign operator). This approach is CORBA-defined, and is both the simplest to use and the most type-safe. However, there are situations where these operators cannot be used. This chapter also describes alternative mechanisms for constructing and interpreting an any.

Consider the following interface:

// IDL

interface AnyDemo {
  void passSomethingIn (in any any_type_parameter);
  ...
}

A client can construct an any to contain a type that can be specified in IDL, and then pass this in a call to operation passSomethingIn(). An application receiving an any must determine the type of value stored by the any and then extract the value.

The IDL type any maps to the C++ class CORBA::Any. Refer to the Orbix C++ Edition Programmer's Reference for the full specification of this class. This class contains some private member data, accessible via public accessor functions, that store both the type of the any and its value. The type is stored as a CORBA::TypeCode, and the value is stored as a void*.

Inserting Data into an Any with operator<<=()

The C++ class CORBA::Any contains a number of left-shift assign operators (<<=) that enable you to assign a value to an any. An overloaded version of operator<<=() is provided for each of the basic IDL types such as long, unsigned long, float, double, string and so on. In addition, the Orbix IDL compiler can generate such an operator for each user-defined type that appears in an IDL specification.


Note:   Operators for user-defined type are generated only if the -A switch is passed to the IDL compiler. Refer to Appendix A, "Orbix IDL Compiler Options" on page 441 for more details.

The IDL definition for the example used in this chapter is as follows:

// IDL 
// In file anydemo.idl

// Illustrates user-defined types and anys.
typedef sequence<long> LongSequence;
...
interface AnyDemo {
  // Takes in any type that can be specified in IDL.
  void passSomethingIn (in any any_type_parameter);

  // Passes out any type specified in IDL.
  void getSomethingOut (out any any_type_parameter);

  // Passes in an any type and passes out an any
  // containing a different type.
  void passSomethingInOut (
    inout any any_type_parameter);
};

A version of the code for the example described in this chapter is available in demos\anydemo directory of your Orbix installation.

Inserting a Basic Type

Orbix provides a pre-defined overloaded version of operator<<=() for basic IDL types such as long, unsigned long, float, double string and so on.

Assume that a client programmer wishes to pass an any containing an IDL short (or, in C++, a CORBA::Short) as the parameter to the passSomethingIn() operation. The client can use the following operator, which is a standard member of the class CORBA::Any:

void operator<<=(CORBA::Short s);

Inserting a User-Defined Type

If the client wishes to pass a more complex user-defined type, such as LongSequence (in file anydemo.idl), it can use the following generated operators:

void operator<<=(CORBA::Any& a,
              const LongSequence& t);

Using this operator, you can write the following code:

// C++
// In file anydemo_menu.cxx.

// Builds an any containing a sequence of type
// LongSequence and then calls passSomethingIn. 
void AnyDemoMenu::do_send_sequence() {
  try {
    CORBA::Any a;

    // Build a sequence of length 2.
    LongSequence sequence_to_insert(2);
    sequence_to_insert.length(2);

    // Insert a value into the sequence.
    sequence_to_insert[0] = 1;
    sequence_to_insert[1] = 2;

    // Use operator<<=() to insert the sequence 
    // into the any.     a<<=sequence_to_insert;     // Print out the contents of the sequence.     cout << "Call passSomethingIn with sequence
      contents:" << sequence_to_insert[0] << " "
      << sequence_to_insert[1] << endl << endl;     // Now invoke passSomethingIn.     m_any_demo->passSomethingIn (a);   }   catch (const CORBA::SystemException& se) {   ...   } }

These operators provide a type-safe mechanism for inserting data into an any. The correct operator is called based on the type of the value being inserted. Furthermore, if an attempt is made to insert a value that has no corresponding IDL type, this results in a compile-time error.

Using the left-shift assign operator to insert a value into an any sets both the value of the CORBA::Any and the CORBA::TypeCode property for the CORBA::Any.

Each left-shift assign operator makes a copy of the value being inserted; for example, in the case of object references, _duplicate() is used. The CORBA::Any is then responsible for the memory management of the copy. Previous values held by the CORBA::Any are properly deallocated; for example, using CORBA::release() in the case of object references.

Refer to "Other Ways to Construct and Interpret an Any" on page 245 for details of how to insert boolean, char, array and octet values.

Interpreting an any with operator>>=()

The C++ class CORBA::Any contains several right-shift assign operators (>>=) that enable you to extract the value stored in an any. These operators correspond to the basic IDL types such as long, unsigned long, float, double, string and so on. As with operator<<=(), the IDL compiler can generate an operator>>=() for each user-defined type that appears in an IDL specification. These additional operators are only generated if the -A switch is specified to the IDL compiler.

Interpreting a Basic Type

The following example illustrates the use of the right-shift assign operators to extract the value stored in an any. Each operator>>=() returns a CORBA::Boolean value to indicate whether or not a value of the required type can be extracted from the any. Each operator>>=() returns 1 if the any contains a value whose CORBA::TypeCode matches the type of the right-hand parameter; and returns 0 otherwise. You can extract a value as follows:

// C++
// In file anydemo_menu.cxx.

// Shows how an any is passed as an out parameter. 
void AnyDemoMenu::do_get_any() {

  1.   CORBA::Any* any_type_parameter;
    
      cout << "Call getSomethingOut" << endl;
      m_any_demo->getSomethingOut(any_type_parameter);
    
      // Assumes that the server passes a string.
      char*  extracted_string = 0;
       
    
  2.   if (*any_type_parameter >>= extracted_string) {
        // Print out the string.
        cout << "any out parameter contains a string with
        value :" << extracted_string << endl << endl;   }   else {   // Error message.     cout << "unexpected value contained in any"
        << endl;   } }

This code is explained as follows:

  1. The CORBA::Any variable retains ownership of the memory it returns when operator>>=() is called. Because the memory is managed by the CORBA::Any type there is no need for you to manage the memory.
  2. The function operator>>=() is used to interpret the contents of the any parameter. If successful, the operator causes the extracted pointer to point to the memory storage managed by the any.

Interpreting a User-Defined Type

More complex, user-defined types can also be extracted using the right-shift operators generated by the IDL compiler. For example, the LongSequence IDL type from "Inserting a User-Defined Type" on page 241:

// IDL
typedef sequence<long> LongSequence;

You can extract a LongSequence from a CORBA::Any as follows:

void AnyDemoMenu::do_get_any() {
  CORBA::Any* any_type_parameter;

  cout << "Call getSomethingOut" << endl;

  m_any_demo->getSomethingOut(any_type_parameter);

  LongSequence*  extracted_sequence = 0

  if (*any_type_parameter>>= extracted_sequence) {
    cout << "any out parameter contains a sequence
    with value :" << extracted_sequence << endl
    << endl;   }   else {     cout << "unexpected value contained in any"     << endl;   } }

The generated right-shift operator for user-defined types takes a pointer to the generated type as the right-hand parameter. If the call to the operator is successful, this pointer points to the memory managed by the CORBA::Any.

No attempt should be made to delete or otherwise free the memory managed by the CORBA::Any. Extraction into a _var variable violates this rule, because the _var variable attempts to assume ownership of the memory. Furthermore, it is an error to attempt to access the storage associated with a CORBA::Any after the CORBA::Any variable has been deallocated.

Other Ways to Construct and Interpret an Any

This section presents a number of other ways to construct and interpret an any. You should use the >>= and <<= operators wherever possible, but there are occasions when you must use a more complex approach.

Inserting Values at Construction Time

Instead of creating a CORBA::Any variable using the default constructor, and then inserting a value using operator<<=(), an application can specify the value and its type when the CORBA::Any is being constructed. This alternative constructor has the following signature:

// C++
Any(CORBA::TypeCode_ptr tc, void* value,
          CORBA::Boolean release = 0);

This is not used normally, because it is more difficult to use than operator<<=(), and because it is not type-safe. Specifically, the type of the value passed to the value parameter may not match the type passed in parameter tc. A mismatch is not detected because the value parameter is of type void* and this leads to subsequent errors.

However, there are some types that must be inserted in this way, for example bounded strings. Both bounded and unbounded IDL strings map to char* in C++, and hence both cannot be inserted using operator<<=(). This operator is used to insert unbounded strings only. A CORBA::Any containing a bounded string must be created using a specific constructor. You can use the function CORBA::Any::replace() to make assignments. Refer to "Low Level Access to a CORBA::Any" on page 248 for more details.

For example, you can construct a CORBA::Any variable to contain a bounded string as follows:

// C++
// In file anydemo_menu.cxx.

// Insert a bounded string into an any using the
// constructor. void AnyDemoMenu::do_send_bounded_string() {   try {     // Allocate the correct memory for the string.
  1.         char* string_to_insert =
              CORBA::string_alloc(string_length);
        strcpy(string_to_insert,"Making Software
                          Work Together (TM)");     // Call to constructor.
  1.         CORBA::Any a(_tc_BoundedString,& string_to_insert, 1);
        
        // Invoke passSomethingIn as normal.
        cout << "Call passSomethingIn with string value :"   
        << string_to_insert << endl << endl;
        m_any_demo->passSomethingIn (a);
      }
      catch (const CORBA::SystemException& se) {
        cerr << "System exception: Call passSomethingIn
        with a string failed" << endl;     cerr <<& sysEx;   }   catch (const CORBA::Exception& se) {     cerr << "Exception: Call passSomethingIn with a
        string failed" << endl;     cerr <<& sysEx;   }   catch (...) {     cerr << "Unexpected exception: Call
        passSomethingIn with a string failed" << endl;   } }

This code is explained as follows:

  1. Because this example uses a bounded string, you must ensure that the string is allocated the appropriate amount of memory. The constant string_length is defined in anydemo.idl.
  2. The first parameter to the CORBA::Any constructor is a pseudo-object reference for a CORBA::TypeCode. In this case, the constant _tc_BoundedString is passed. This constant is generated by the IDL compiler.

    The second parameter is a pointer to the value to be inserted into the CORBA::Any; in this case string_to_insert. This value should be of the type specified by the CORBA::TypeCode_ptr parameter. The behaviour is undefined if the CORBA::TypeCode_ptr and the value parameters do not agree. When constructing CORBA::Anys for string types, the second parameter is of type char**.

    The third parameter, release, specifies which code assumes ownership of the memory occupied by the value in the CORBA::Any variable (string_to_insert). If this is 1 (true), the CORBA::Any assumes ownership of the storage pointed to by the value parameter. If this parameter is 0 (false), the caller must manage the memory associated with the value. The default is zero.

    In this example, the CORBA::Any assumes ownership of the memory associated with the variable string_to_insert: the application code is not required to free this memory.

Low Level Access to a CORBA::Any

Class CORBA::Any provides three type-unsafe functions enabling low level access to an Any. These are defined as follows:

// C++
void replace(CORBA::TypeCode_ptr, void* value,
                CORBA::Boolean release = 0);

CORBA::TypeCode_ptr type() const;

const void* value() const;

replace()

The replace() function is only intended for use with types that cannot use the type-safe operator interface. It can be used at any time after construction of a CORBA::Any to replace the existing CORBA::TypeCode and value. Like the various <<= operators, it releases the previous CORBA::TypeCode and if necessary, deallocates the storage previously associated with the value. The release parameter has the same semantics as the release parameter of the CORBA::Any constructor described in "Inserting Values at Construction Time" on page 245.

type()

The type() function returns an object reference for a CORBA::TypeCode that describes the type of the CORBA::Any. As with all object references, the caller must release the reference when it is no longer needed, or assign it to a CORBA::TypeCode_var variable for automatic management.

value()

The value() function returns a pointer to the data stored in the CORBA::Any, or, if no value is stored, it returns the null pointer. This value may be cast to the appropriate C++ type depending on the CORBA::TypeCode of the CORBA::Any. The rules for the actual C++ type returned for each different IDL type are listed in the entry for CORBA::Any in the Orbix C++ Edition Programmer's Reference.

If the CORBA::Any contains an object reference for an object whose type is unknown at compile time, the type() function returns a reference for a CORBA::TypeCode object that is equal to the _tc_object typecode constant. The value() function returns a void* that can be cast to a CORBA::Object_ptr*.

Example of Using type() and value()

The following example determines the type of an any by comparing the contents of the any with the typecode constant for a bounded string:

// C++
// In file anydemo_impl.cxx.

void AnyDemoImpl::passSomethingIn(
  const CORBA::Any& any_type_parameter,
  CORBA::Environment& )
  throw(CORBA::SystemException) {   ...   CORBA::TypeCode_ptr type= any_type_parameter.type();   // Checks if the any contains a bounded string.   if (type->equal(_tc_BoundedString)) {
     // Returns a void pointer to the bounded string.     char** any_contents =       (char**)any_type_parameter.value();     const char* bounded_string = *any_contents;     // Print out the contents.     cout << "passSomethingIn extracted a bounded
      string of length " << strlen(bounded_string)
      << " and value " << bounded_string << endl
      << endl;   }   else {     // Error message.     cout << "passSomethingIn: unexpected value"
    << endl;   } }

Orbix defines a typecode constant for each built-in type, and you can instruct the IDL compiler to generate typecode constants for each user-defined type. This is discussed in more detail in Chapter 11, "The TypeCode Data Type" on page 231.

Refer to the Orbix Reference Guide for more details on the replace(), type() and value() functions.

Inserting and Extracting Array Types

Recall that IDL arrays are mapped to regular C++ arrays. This presents a problem for the type-safe operator interface to CORBA::Any. C++ array parameters decompose to a pointer to their first element, so you cannot use the operators to insert or extract arrays of different lengths.

Nevertheless, arrays can be inserted and extracted using the operators, because a distinct C++ type is generated for each IDL array--specifically to help with insertion and extraction into or out of CORBA::Any variables. The name of this type is the name of the array followed by the suffix "_forany".

The following example shows type-safe manipulation of arrays and CORBA::Anys:

// IDL
typedef long longArray[2][2];

// C++
longArray_forany m_array = { {14, 15}, {24, 25} };

// Insertion:
CORBA::Any a;
if (a <<= m_array) {
  cout << "Success!" << endl;
}

// Extraction:
longArray_forany extractedValue;
if (a >>= extractedValue) {
  cout << "Element [1][2] is "
      << extractedValue[1][2] << endl;
}

These types, like the array _var types, provide an operator[]() to access the array members, but the _forany types do not delete any storage associated with the array when they are themselves destroyed. This is a good match for the semantics of operator>>=(). The CORBA::Any retains ownership of the memory returned by the operator. There is therefore no memory leak in this code sample.

Inserting and Extracting boolean, octet and char

The standard CORBA IDL to C++ mapping does not require that the IDL types boolean, octet and char map to distinct C++ types. Therefore, it is not possible to insert and extract each of these using operator<<=() and operator>>=(). Remember that the overloaded right-shift and left-shift assignment operators are distinguished based on the type of the right-hand argument.

In Orbix, the types boolean and octet map to the same underlying C++ type (unsigned char). Type char maps to a different type (C++ char), so a separate operator could have been provided for it, but this would not be CORBA compliant.

The distinction is achieved by using helper types that are nested within the C++ class CORBA::Any. These helper types are structs; refer to the entry for CORBA::Any in the Orbix C++ Edition Programmer's Reference for details on their syntax. Left-shift and right-shift assignment operators are provided for each of these helper types.

These helper classes can be used as follows:

// C++
CORBA::Any a;

// Insert a boolean into the CORBA::Any a:
CORBA::Boolean b = 1;
a<<=CORBA::Any::from_boolean(b);

// Extract the boolean.
CORBA::Boolean extractedValue;
if (a>>=CORBA::Any::to_boolean(extractedValue)){
  cout<<"Success!"<<endl;
}
// Insert an octet into the CORBA::Any a:
CORBA::Octet o = 1;
a<<=CORBA::Any::from_octet(o);

// Extract the octet from a:
CORBA::Octet extractedValue;
if (a>>=CORBA::Any::to_octet(extractedValue)) {
  cout<<"Success!"<<endl;
}

// Insert a char into the CORBA::Any a:
CORBA::Char c=`b';
a<<=CORBA::Any::from_char(c);

// Extract the char from a:
CORBA::Char extractedValue;
if (a>>=CORBA::Any::to_char(extractedValue)) {
  cout<<"Success!"<<endl;
}

Any Constructors, Destructor and Assignment

In addition to the functionality already described, the C++ class CORBA::Any also contains the following:

Any as a Parameter or Return Value

The mappings for IDL any operation parameters and return value are illustrated by the following IDL operation:

// IDL
any op(in any a1, out any a2, inout any a3);

This maps to:

// C++
CORBA::Any* op(const CORBA::Any& a1,
    CORBA::Any*& a2, CORBA::Any& a3);

Because both return values and out parameters map to pointers to CORBA::Any, a CORBA::Any_var class is provided that manages the memory associated with this pointer. The CORBA::Any_var class calls the C++ operator delete on its associated CORBA::Any* when it is itself destroyed; for example, by going out of scope.



support@iona.com
Copyright © 2000, IONA Technologies PLC.