Expose multiple interfaces
If an object must expose multiple behaviors, the object must be able to return multiple interfaces. To enable an object to return multiple interfaces, you can derive the object from the various interfaces by using multiple inheritance, or derive the object from a separate implementation hierarchy and derive nested classes from the appropriate interfaces.
The nested class solution, which is used by the , has
the following benefits:
- It allows the COM-compliant exposure of multiple interfaces.
- It allows delegation, the ability of a container class to expose an interface belonging to a class it contains. For more details, see Object Containment and Delegation.
- It creates multiple implementations of reference counting code for each interface, making it easier to track the reference counts for each interface. By tracking references to individual interfaces, your application can optimize object storage by allocating or deallocating part of an object based on whether a specific interface has an outstanding reference count. For example, if an object exposes ITLargeObject and it uses ITLargeObjectManager to implement its functions, it can call ITLargeObjectManager::Close() when the ITLargeObject interface reference count drops to 0 so that the number of open smart large objects is minimized.
For a demonstration of the nested-class model, see the ifval.cpp example. The ifval.cpp example is driven by the contain.cpp example application.
The following code excerpts from ifval.cpp illustrate
the implementation of an array of integers value object that exposes
both ITDatum and ITContainCvt interfaces:
- Define the private data structures.
typedef mi_integer bitarray_t;
This structure is not exposed to the application.
- Define the object class. Instead of using inheritance on the parent
object, use nested classes to define the individual interfaces.
class Bitarray { public: // ITDatum-derived nested class. This just passes work through // the parent pointer into the parent object class XITDatum : public ITDatum { public: // ... } datum_interface; // ITContainCvt-derived nested class // This just passes work through the parent // pointer into the parent object class XITContainCvt : public ITContainCvt { public: // ... } containcvt_interface; // ... };
- Build the object.
// Implementation Bitarray::Bitarray(ITMVDesc *mv) : refcount(1), typeinfo(*mv->vf_origtypeinfo), conn(*mv->vf_connection), isupdated(FALSE) { // NULL? isnull = mv->vf_libmivaluetype == MI_NULL_VALUE; // set up interfaces datum_interface.parent = this; containcvt_interface.parent = this; if(!isnull) value = *(bitarray_t *)mv->vf_data; }
- Define the class factory mapping and entry point.
ITFactoryList BitarrayFactory("bitarray", Bitarray::MakeValue); // Create the Bitarray object, and return pointer to // it's ITValue implementation ITValue * Bitarray::MakeValue(ITMVDesc *mv) { Bitarray *impl = new Bitarray(mv); return (ITValue *)&impl->datum_interface; }
- Define the base class methods for objects and return the address
of the nested interfaces when requested by the application.
ITOpErrorCode Bitarray::QueryInterface(const ITInterfaceID &iid, void **ifptr) { int result = IT_QUERYINTERFACE_SUCCESS; // Return different interfaces as appropriate by referencing // nested class members. switch (ITIIDtoSID(iid)) { case ITEssentialSID: case ITValueSID: case ITDatumSID: *ifptr = (void *) &datum_interface; break; case ITContainCvtSID: *ifptr = (void *) &containcvt_interface; break; default: result = IT_QUERYINTERFACE_FAILED; *ifptr = NULL; break; } if (result == IT_QUERYINTERFACE_SUCCESS) AddRef(); return result; }
This object does not support delegation, so there is only one real QueryInterface implementation on the object.
- Define the reference counting code.
unsigned long Bitarray::AddRef() { return ++refcount; } unsigned long Bitarray::Release() { if (--refcount <= 0) { delete this; return 0; } else { return refcount; } }
- Implement the ITDatum interface methods.
const ITString & Bitarray::Printable() { if(IsNull()) return printable_value = "null"; char buf[32]; ostrstream cstream(buf, sizeof buf); cstream << value << ends; return printable_value = cstream.str(); }
- Implement the ITContainCvt interface.
ITBool Bitarray::ConvertTo(long item, int &dbvalue) { if (IsNull() || item >= NumItems()) return false; dbvalue = !!(value & (1 << (NBITS - 1 - item))); return true; }
This interface converts the member value from the object into a host variable.
- Declare pass-through methods for the nested interfaces. The methods
call the corresponding method in the parent class.
ITOpErrorCode Bitarray::XITDatum::QueryInterface(const ITInterfaceID &ifiid, void **resultif) { return parent->QueryInterface(ifiid, resultif); } unsigned long Bitarray::XITDatum::AddRef() { return parent->AddRef(); } unsigned long Bitarray::XITDatum::Release() { return parent->Release(); } const ITString & Bitarray::XITDatum::Printable() { return parent->Printable(); } const ITTypeInfo & Bitarray::XITDatum::TypeOf() { return parent->TypeOf(); } ITBool Bitarray::XITDatum::IsNull() { return parent->IsNull(); } ITBool Bitarray::XITDatum::SameType(ITValue *v) { return parent->SameType(v); } ITBool Bitarray::XITDatum::CompatibleType(ITValue *v) { return parent->CompatibleType(v); } ITBool Bitarray::XITDatum::Equal(ITValue *v) { return parent->Equal(v); } ITBool Bitarray::XITDatum::LessThan(ITValue *v) { return parent->LessThan(v); } ITBool Bitarray::XITDatum::IsUpdated() { return parent->IsUpdated(); } ITBool Bitarray::XITDatum::FromPrintable(const ITString &v) { return parent->FromPrintable(v); } ITBool Bitarray::XITDatum::SetNull() { return parent->SetNull(); } ITOpErrorCode Bitarray::XITContainCvt::QueryInterface(const ITInterfaceID &ifiid, void **resultif) { return parent->QueryInterface(ifiid, resultif); } unsigned long Bitarray::XITContainCvt::AddRef() { return parent->AddRef(); } unsigned long Bitarray::XITContainCvt::Release() { return parent->Release(); } ITBool Bitarray::XITContainCvt::ConvertTo(long item, int &value) { return parent->ConvertTo(item, value); } long Bitarray::XITContainCvt::NumItems() { return parent->NumItems(); } ITBool Bitarray::XITContainCvt::ConvertFrom(long item, int value) { return parent->ConvertFrom(item, value); }