Value objects and connection events

Value objects are created in the following circumstances:
  • Query objects create instances of the top-level rows.
  • Complex objects (rows and collections) create instances of their members.
  • Prepared query objects create instances of their parameters.

If the value object encapsulates a small, fixed-size datum, it can keep a local copy of that datum. If the datum is large, of variable size, or represents a complex type, the value object keeps a pointer to it. To ensure that this pointer continues to be valid even after all the references to the object that owns the datum memory are released, ITMVDesc contains a pointer to the ITPreserveData interface of that object (ITMVDesc.vf_preservedata). The value object keeps this pointer and use it to call the AddRef() function when it is created. When the value object is deleted, it calls the Release() function by using the ITPreserveData pointer.

If the value object that keeps a local copy of a datum is updated, it modifies that local copy. If the value object keeps a pointer to the datum, it cannot modify that datum—it must create an instance of that datum and call the Release() function on the ITPreserveData pointer passed to it in ITMVDesc. The value object ensures that the IsUpdated() function of its ITValue interface returns TRUE if it is modified. The instance of a datum allocated on update is removed when the value object is removed. The rowref.cpp example illustrates this "allocate-on-update" technique.

To monitor connection events, a value object that keeps a pointer to row data can maintain a connection stamp. This connection stamp, of type ITConnectionStamp, is checked before the row data pointer is dereferenced. The ITConnectionStamp::EqualConnInstance method of the ITConnectionStamp class can be used to tell if the connection is the same instance as that called by another connection stamp.

Use of the connection stamp and ITPreserveData interface is demonstrated in the rowref.cpp example source file, which is included in the contain2.cpp example application. The following code excerpts illustrate how the rowref.cpp example preserves a reference on its underlying data instead of copying the data:
  1. Add a member variable to hold the ITPreserveData interface pointer and connection stamp.
    class Bitarray
    {
        ITPreserveData *preservedata;   // reference counter on datum
        ITConnectionStamp stamp;    // connection stamp
    };
  2. Initialize the preservedata member with the value from the descriptor, add a reference to it, and make a copy of the connection stamp.
    Bitarray::Bitarray(ITMVDesc *mv)
        : refcount(1),
          typeinfo(*mv->vf_origtypeinfo),
          conn(*mv->vf_connection),
          stamp(mv->vf_connection->GetStamp()),
          preservedata(mv->vf_preservedata),
          isupdated(FALSE),
          pvalue(0)
    {
        // NULL?
        isnull = mv->vf_libmivaluetype == MI_NULL_VALUE;
    
        // set up interfaces
        datum_interface.parent = this;
        containcvt_interface.parent = this;
    
        if(!isnull)
            {
            pvalue = (bitarray_t *)mv->vf_data;
            // We are holding an outstanding reference to datum, so 
            // increment its owner’s reference count. 
            // Note that preservedata can be null for null objects.
            preservedata->AddRef();
    }
  3. When the object is being deleted, release the preservedata interface.
    Bitarray::~Bitarray()
    {
        if(isupdated)
            delete pvalue;
        else if(preservedata)
            preservedata->Release();
    }
  4. Before any attempt to de-reference the value member pointer, first check the connection stamp to ensure that the underlying data is still valid.
    const ITString &
    Bitarray::Printable()
    {
        // If the underlying data has changed its not safe to proceed.
        if (!stamp.EqualConnInstance(conn.GetStamp()))
            return ITString::Null;
    
        if(IsNull())
          return printable_value = "null";
    
        char buf[32];
        ostrstream cstream(buf, sizeof buf);
        cstream << *pvalue << ends;
        return printable_value = cstream.str();
    }