[Enterprise Extensions only]

C++ bindings for CORBA Sequence types

An IDL sequence type is mapped to a C++ class that behaves like an array with a current length (how many elements have been stored) and a maximum length (how much storage is currently allocated). The array indexing operator [] is used to read and write sequence elements. (Indexing begins at zero.) It is the programmer's responsibility to check the current sequence length or maximum to prevent accessing the sequence beyond its bounds. The length and maximum of the sequence are not automatically increased to accommodate new elements; the programmer must explicitly increase them.

The maximum length of a bounded sequence is implicit in the sequence class's type and cannot be changed. The initial maximum length of an unbounded sequence is set to zero by the default constructor, or initialized by the programmer using a non-default constructor. Setting the initial maximum length of an unbounded sequence using the non-default constructor causes storage to be allocated for the specified number of sequence elements.

Sequence classes provide an overloaded member function length that either returns or sets the length of the sequence. Setting the length of an unbounded sequence to a value larger than the current maximum causes the sequence to allocate new storage of the required size, copy any previous sequence elements to the new storage, free the old storage (if any), and reset the maximum to the new length. Sequence classes also provide allocbuf and freebuf member functions for explicitly allocating/freeing the sequence's storage buffer. Decreasing a sequence's length does not cause any storage to be deallocated, but any orphaned sequence elements are no longer accessible, even if the sequence length is subsequently increased.

Sequences may or may not manage (own) the storage that contains their elements, and the elements themselves By default, a sequence manages this storage, but a release constructor parameter allows client programmers to request otherwise (when passing in a buffer explicitly allocated using the allocbuf function).

The following IDL:

typedef sequence s1; // unbounded sequence

is mapped to the following C++ sequence class:

class s1 
{
   public:
   s1();// default constructor
   s1(CORBA::ULong max);// "max" constructor
   s1(CORBA::ULong max, CORBA::ULong length, 
   T* data, CORBA::Boolean release=0);
   // "data" constructor
   s1(const s1&);// copy constructor
   s1 &operator= (const s1&);   // assignment operator
   ~s1();// destructor
   CORBA::ULong maximum() const;
   CORBA::ULong length() const;
   void length(CORBA::ULong len);
   T& operator[] (CORBA::ULong index);
   const T& operator[] (CORBA::ULong index) const;
   static T* allocbuf(CORBA::ULong nelems);
   static void freebuf(T* data);
};

The default constructor sets the length and maximum to zero. (For a bounded sequence, the default constructor sets the maximum to the sequence bounds and allocates storage for the maximum number of elements, which the sequence owns.)

The "max" constructor sets the initial sequence maximum and allocates a storage buffer for the specified number of sequence elements, which the sequence owns. The length of the sequence is initialized to zero.

Note: This method is not available for bounded sequences.

The "data" constructor sets the initial length and maximum of the sequence, as well as its initial contents. (For bounded sequences, the maximum cannot be set by the "data" constructor.) The input storage should match the specified sequence maximum. Ownership of the input storage is indicated by the "release" parameter. Passing release=1 specifies that the storage was allocated using s1::allocbuf, that the sequence should delete the storage and the sequence elements when the sequence is deleted or when the storage needs to be reallocated, and that the caller will not directly access the storage after the call (because the sequence is free to delete it at any time). In general, sequences constructed with release=0 should not be passed as inout parameters, because the callee must assume that the sequence owns the sequence elements.

The copy constructor creates a new sequence with the same maximum and length as the input sequence and copies the sequence elements to storage that the sequence owns. The assignment operator performs a deep copy, releasing the previous sequence elements if necessary. It behaves as if the destructor were run, followed by the copy constructor.

The destructor destroys each of the sequence elements (from zero through length-1), if the sequence owns the storage.

The allocbuf function allocates enough storage for the specified number of sequence elements; the return value can then be passed to the "data" constructor. Each sequence element is initialized using its default constructor; string elements are initialized to NULL; object reference elements are initialized to nil object references. NULL is returned if storage cannot be allocated for any reason. If ownership of the allocated buffer is not transferred to a sequence using the "data" constructor with release=1, the buffer should be subsequently freed using the freebuf function. The freebuf function insures that each sequence element's destructor is run (or, for strings, that CORBA::string_free is called, or for object references, that CORBA::release is called) before the buffer is deleted. The freebuf function ignores NULL pointers passed to it. Neither allocbuf nor freebuf throw CORBA exceptions.

As with structs, sequences that manage their elements use special auxiliary classes for automatic storage management of String and object reference sequence elements. These auxiliary classes manage Strings and object references just as the associated _var classes do.

For a storage-managing sequence whose elements are object references, when assigning a value to an element, the assignment operator will automatically release the previous value, if any. When assigning an object reference pointer to such a sequence element, the sequence assumes ownership of the pointer (no _duplicate is done), and the application should no longer access the pointer directly. (If this is not the desired behavior, then the caller can explicitly _duplicate the object reference before assigning it to the sequence element.) However, when assigning to such an object reference sequence element from a _var object or from another struct, union, array, or sequence (rather than from an object reference pointer), a _duplicate is done automatically.

For a storage-managing sequence whose elements are Strings, when assigning a value to such an element or deleting the sequence, any previously held (non-null) char* is automatically freed. As when assigning to String_vars, assigning a char* to a string element does not make a copy, but assigning a const char *, a String_var, or another struct/union/array/sequence String member does automatically make a copy. Thus, one should never assign a string literal (such as "abc") to such an element without an explicit cast to const char*. When assigning a char* that occupies static storage (rather than one that was dynamically allocated), the caller can use CORBA::string_dup to duplicate the string before assigning it.

There is a corresponding _var type defined for every sequence class.The _var type for a sequence provides an overloaded operator[] that forwards the operator the underlying sequence.

Following is an example that illustrates loading and accessing the elements of a sequence. This example illustrates a recursive sequence (whose entries are structs of the same type that contain the sequence). The IDL for the example is shown below:

struct S 
{
   long sf1;
   sequence sf2;
};
typedef sequence Sseq;

The following is an example program that creates and loads a sequence of type Sseq and then prints out its contents.

#include seq_C.cpp
#include stdio.h
main()
{
   int i,j;
   Sseq seq;      // create an Sseq
   seq.length(3); // set length of seq to 3
   for (i=0; i<3; i++) { // index the three S structs in seq
   seq[i].sf1 = i;    // place a number in the i-indexed struct
   seq[i].sf2.length(i+1); // set length of the sequence in
   //   the i-indexed struct
   for (j=0; j<i+1; j++) // index the i+1 S structs in the sequence
   //  in the i-indexed struct
   seq[i].sf2[j].sf1 = (i+1)*10+j; // place a number in 
   //   the j-indexed struct 
}
// OK. Print out what you have created!
printf("seq = (%d sequence elements)\n", seq.length());
for (i=0; i<3; i++) 
{
   printf("   struct[%d] = {\n", i);
   printf("      sf1 = %d\n", seq[i].sf1);
   printf("      sf2 = (%d sequence elements)\n",
   seq[i].sf2[j].length());
   for (j=0; j<i+1; j++)  
   {
      printf("         struct[%d] = \n",j);
      printf("            sf1 = %d\n", seq[i].sf2[j].sf1);
      printf("            sf2 = (%d sequence elements)\n",
      seq[i].sf2[j].sf2.length());
      printf("         }\n");
      }
   printf("   }\n");
   }
} 

Note that the above program never explicitly constructs any data of type S, even though the sequences contain structs of this type. The reason is that when a sequence buffer is allocated, default constructors are run for each of the buffer elements. So, when the above program sets the length of a sequence of S structs (either at the top level for the seq variable, or for the sf2 field of an S struct in seq), the resulting buffer is automatically populated with default structs of type S.

The output from the above program is:

seq = (3 sequence elements)
   struct[0] = {
      sf1 = 0
      sf2 = (1 sequence elements)
         struct[0] = {
            sf1 = 10
            sf2 = (0 sequence elements)
         }
   }
   struct[1] = {
      sf1 = 1
      sf2 = (2 sequence elements)
         struct[0] = {
            sf1 = 20
            sf2 = (0 sequence elements)
         }
         struct[1] = {
            sf1 = 21
            sf2 = (0 sequence elements)
         }   
   }
   struct[2] = {
      sf1 = 2
      sf2 = (3 sequence elements)
         struct[0] = {
            sf1 = 30
            sf2 = (0 sequence elements)
         }
         struct[1] = {
            sf1 = 31
            sf2 = (0 sequence elements)
         }
         struct[2] = {
            sf1 = 32
            sf2 = (0 sequence elements)
         }
   }