Wednesday, October 21, 2009

CSB Guide


3 4



CSB Guide



I will take a somewhat different approach to source code exposition in this chapter than I have elsewhere in this book. The CSB source code is too voluminous for reproduction and analysis here. The programming guide presented in this section will cover everything you need to know in order to use CSB. I will show you the headers of important CSB templates and data structures. And I will interpret the meaning of all configuration flags and functionals provided by CSB. The CSB source code is included on the companion CD. I hope that the implementation and the comments will help answer any questions you might have. The companion CD also contains the sample project shown toward the end of this chapter in the "Usage Patterns" section.



Philosophy



As a two-way bridging solution, CSB wraps STL containers and iterators in COM interfaces while providing adapters to gain STL-compliant access to collection and enumerator interface pointers. Therefore, you can employ CSB on either one of the two sides of the component equation, or on both sides. For example, you might need CSB to give only COM access to some STL-compliant C++ containers that you've implemented, or you might want to give STL algorithms (either standard ones or your own) access to COM components that you've purchased from a third party. Another common motivation for using CSB is the desire to access your C++ data structures via algorithms in different process spaces or on different machines, even if the flexibility of supporting COM interfaces on these components is not important to you otherwise. To make any of these strategies as easy as possible for you, CSB adopts the goals discussed in the following sections.



Customizability



The various aspects of CSB component operation are controlled via trait functionals. This section describes what these aspects are and how to implement your own traits when necessary.



Ease of Use Through Default Functionality



While you always have the option of supplying your own traits, often you'll simply want to choose a certain default functionality. You do this by incorporating predefined CSB traits into your types. I will describe these default traits later in this section. When appropriate, default functionality is provided in the form of a templated interface method implementation instead of a trait. Scanning the CollectionImpl.h header file for such templates can be instructive and can prevent you from duplicating your efforts.




  • STL as the C++ Component Layer. I chose STL as the standard defining C++ componentization because it appears to be emerging as the lowest common denominator with which other libraries are compatible and because it is part of the C++ standard. Due to this choice, CSB not only gives you access to the STL standard artifacts, it also lets you access any other STL-compliant component library, such as Rogue Wave's Tools.h++ product.


  • ATL as the COM Component Layer. CSB uses ATL to provide COM functionality on top of C++. I believe ATL is the thinnest yet most powerful C++ COM framework—least likely to interfere with the performance and functionality of your C++ objects, and most likely to give you the range of configuration options you need.


  • Portability. CSB was originally conceived during a project intended to be deployed on various COM platforms, including several UNIX platforms. It therefore relies on COM-specific and ATL-specific features (since these are commonly supplied by COM vendors on other platforms), but it avoids nonstandard C++ extensions. This is the reason why I cannot provide an operator like this:







template�<class�TInterface>
IStream&�::operator�>>�(IStream&,�TInterface*&);



To do this, I would need to query the COM object for the IID of TInterface, after it has been loaded from the stream. While Visual C++ allows us to determine the IID of TInterface with __uuidof, this feature is not portable. Therefore, I provide a portable helper template LoadItfPtr instead of a >> operator.



Collection Wrapper



In order to turn an STL-compliant C++ container into a COM object, you first place the C++ container into a simple ATL class. Then you derive that ATL class from CSB's DICollectionImpl template. This template takes one template parameter upon instantiation, the so-called Typer template. DICollectionImpl is defined as follows:





template�<class�T>
��//�TBaseInterface�should�be�derived,�directly�or�indirectly,
��//�from�DICollection;�alternatively,�a�class�with�compatible�vtable
��//�layout�can�be�provided�and�reference�counting�can�be�performed�on�that
��//�interface
class�ATL_NO_VTABLE�DICollectionImpl�:�public�T::TBaseInterface
{
//�Types
public:
����typedef�T::TDerived�����������������TDerived;
����typedef�T::TBaseInterface�����������BaseInterface;
����typedef�T::TTypedCollectionIID������TTypedCollectionIID;
����typedef�T::TContainer���������������TContainer;
����typedef�T::TContainerLock�����������TContainerLock;
����typedef�T::TInserter����������������TInserter;
����typedef�T::TIEnumX������������������TIEnumX;
����typedef�T::TTypedEnumerationIID�����TTypedEnumerationIID;
����typedef�T::TComparisonPred����������TComparisonPred;
����typedef�T::TValueCheckAdjust��������TValueCheckAdjust;
����typedef�T::TCopier������������������TCopier;
����typedef�T::TValueVariantConverter���TValueVariantConverter;
����typedef�T::TExternalType���������� TExternalType;
����typedef�T::TValueExternalConverter��TValueExternalConverter;
����typedef�T::TInsertRemoveNotifier��� TInsertRemoveNotifier;
����typedef�T::TValuePersist������������TValuePersist;
����typedef�T::TIndexLocator����������� TIndexLocator;

protected:
����typedef�DICollectionImpl<T>����DICollectionInst;
����typedef�TContainerLock::t_Lock::t_ReadLock��t_ReadLock;
����typedef�TContainerLock::t_Lock::t_WriteLock�t_WriteLock;

//�Construction
����DICollectionImpl(TContainer&�rcContainer,
���������������������const�TComparisonPred�rcComparisonPred�=
���������������������������TComparisonPred(),
���������������������const�TValueCheckAdjust�rcValueCheckAdjust�=
���������������������������TValueCheckAdjust(),
���������������������const�TContainerLock�rcContainerLock�=
���������������������������TContainerLock(),
���������������������const�TInserter&�rcInserter�=�TInserter(),
���������������������const�TCopier&�rcCopier�=�TCopier(),
���������������������const�TValueVariantConverter&�rcValueVariantConverter
���������������������������=�TValueVariantConverter(),
���������������������const�TValueExternalConverter&�ValueExternalConverter
���������������������������=�TValueExternalConverter(),
���������������������const�TInsertRemoveNotifier&�rcInsertRemoveNotifier�=
���������������������������TInsertRemoveNotifier(),
���������������������const�TValuePersist�rcValuePersist�=�TValuePersist(),
���������������������const�TIndexLocator&�rcIndexLocator�=
���������������������������TIndexLocator());

//�Destruction
����void�FinalRelease();

private:
����DICollectionImpl(const�DICollectionImpl&);
����DICollectionImpl&�operator=�(const�DICollectionImpl&);

//�DICollection
public:
����STDMETHOD(Add)(/*[in]*/�VARIANT�Element);
����STDMETHOD(AddNew)(/*[in,�optional]*/�VARIANT�What,
����������������������/*[out,�retval]*/�VARIANT*�Element);
����STDMETHOD(Change)(/*[in]*/�VARIANT�IndexOrElement,
����������������������/*[in]*/�VARIANT�NewValue);
����STDMETHOD(Contains)(/*[in]*/�VARIANT�Element,
������������������������/*[out,�retval]*/�VARIANT_BOOL*�ReturnVal);
����STDMETHOD(Find)(/*[in]*/�VARIANT�Element,
��������������������/*[out,�retval]*/�VARIANT*�Match);
����STDMETHOD(get_Count)(/*[out,�retval]*/�int*�ReturnVal);
����STDMETHOD(Value)(/*[in]*/�int�Index,
���������������������/*[out,�retval]*/�VARIANT*�Element);
����STDMETHOD(Remove)(/*[in]*/�int�Index);
����STDMETHOD(Clone)(/*[out,�retval]*/�DICollection**�Copy);
����STDMETHOD(Clear)();
����STDMETHOD(_NewEnum)(/*[out,�retval]*/�IEnumVARIANT**�ReturnVal);

//�DICollection�derived�interface�support�(type�safe)
//���Note:�not�declaring�these�methods�virtual�here�means�they�won't
//���������be�compiled�into�the�instantiation�in�case�the�base�interface
//���������contains�no�corresponding�member�and/or�their�implementation�is
//���������not�type�compatible�with�the�container
����HRESULT�STDMETHODCALLTYPE�TypedAdd(/*[in]*/�TExternalType�Element);
����HRESULT�STDMETHODCALLTYPE�TypedChange(/*[in]*/�TExternalType�Element,
������������������������������������������/*[in]*/�TExternalType�NewValue);
����HRESULT�STDMETHODCALLTYPE�TypedContains(/*[in]*/�TExternalType�Element,
��������������������������������������������/*[out,�retval]*/
��������������������������������������������VARIANT_BOOL*�ReturnVal);
����HRESULT�STDMETHODCALLTYPE�TypedFind(/*[in]*/�TExternalType�Element,
����������������������������������������/*[out,�retval]*/
����������������������������������������TExternalType*�Match);
����HRESULT�STDMETHODCALLTYPE�TypedValue(/*[in]*/�int�Index,
�����������������������������������������/*[out,�retval]*/
�����������������������������������������TExternalType*�Element);
����HRESULT�STDMETHODCALLTYPE�TypedRemove(/*[in]*/�TExternalType�Element);
����HRESULT�STDMETHODCALLTYPE�_TypedNewEnum(/*[out]*/�TIEnumX**�ReturnVal);
����HRESULT�STDMETHODCALLTYPE�_NewEnumNoCopy(/*[out]*/
���������������������������������������������TIEnumX**�ReturnVal);

//�ISupportErrorInfo�-�base�class�support
����HRESULT�InterfaceSupportsErrorInfo(REFIID�riid)�const;

//�Persistence�support
//��Virtual�not�needed:�methods�are�called�through�derived�class�TDerived
����void�PersistLoad(IStream&�riStream);
����void�PersistSave(IStream&�riStream)�const;

//�Auxiliary�C++�interface

//�Implementation
private:
����TContainer&������������������m_rcContainer;

protected:
����TContainerLock���������������m_cContainerLock;
����TInserter��������������������m_cInserter;
����TComparisonPred��������������m_cComparisonPred;
����TValueCheckAdjust������������m_cValueCheckAdjust;
����TCopier����������������������m_cCopier;
����TValueVariantConverter�������m_cValueVariantConverter;
����TValueExternalConverter������m_cValueExternalConverter;
����TInsertRemoveNotifier��������m_cInsertRemoveNotifier;
����TValuePersist����������������m_cValuePersist;
����TIndexLocator����������������m_cIndexLocator;
};



The role of the Typer is to relay all trait information to DICollectionImpl. There are currently two templates that, when instantiated, form valid Typers. The first, SGenericTyper, is for generic containers; the second, SItfPtrTyper, has been simplified for use with collections that contain some type of interface pointer. In the list that follows, we describe the role of each trait type defined in a Typer. Note that the Typer templates adopt default parameters for many of these trait types. The interface pointer Typer predetermines some of the trait types in a manner that can and should not be changed, but also requires the IID of the interface that is being collected. While a Typer carries only type information and no actual instance of any functional, the DICollectionImpl constructor gives you the opportunity to supply an argument from which to construct each trait. Therefore, you can build stateful traits and have their state carried forward by CSB. The generic Typer template is defined like this:





template�<class�_TDerived,
����������class�_TBaseInterface,
����������const�IID*�ptTypedCollectionIID,
����������class�_TContainer,
����������class�_TContainerLock,
����������class�_TInserter,
����������class�_TIEnumX,
����������const�IID*�ptTypedEnumerationIID,
����������class�_TComparisonPred�=�std::equal_to<_TContainer::value_type>,
����������class�_TValueCheckAdjust�=�TValueGate<_TContainer::value_type>,
����������class�_TCopier�����������=�TCopier<_TContainer::value_type>,
����������class�_TValueVariantConverter�=
��������������������TCanonicalVariantVert<_TContainer::value_type>,
����������class�_TExternalType�����=�_TContainer::value_type,
����������class�_TValueExternalConverter�=
��������������������TIdentaVert<_TContainer::value_type,�
���������������������������������_TExternalType,�_TCopier>,
����������class�_TInsertRemoveNotifier�=
��������������������TInsertRemoveNotifier<_TContainer::value_type>,
����������class�_TValuePersist�=�TStreamOpRouter<_TContainer::value_type>,
����������class�_TIndexLocator�=�TIndexLocator<_TContainer>�>
struct�SGenericTyper
{
����typedef�_TDerived�������������������������TDerived;
����typedef�_TBaseInterface�������������������TBaseInterface;
����typedef�TIdToType<ptTypedCollectionIID>���TTypedCollectionIID;
����typedef�_TContainer�����������������������TContainer;
����typedef�_TContainerLock�������������������TContainerLock;
����typedef�_TInserter������������������������TInserter;
����typedef�_TIEnumX��������������������������TIEnumX;
����typedef�TIdToType<ptTypedEnumerationIID>��TTypedEnumerationIID;
����typedef�_TComparisonPred������������������TComparisonPred;
����typedef�_TValueCheckAdjust����������������TValueCheckAdjust;
����typedef�_TCopier��������������������������TCopier;
����typedef�_TValueVariantConverter�����������TValueVariantConverter;
����typedef�_TExternalType��������������������TExternalType;
����typedef�_TValueExternalConverter����������TValueExternalConverter;
����typedef�_TInsertRemoveNotifier������������TInsertRemoveNotifier;
����typedef�_TValuePersist��������������������TValuePersist;
����typedef�_TIndexLocator��������������������TIndexLocator;
};



The Typer already specialized for use with interface pointers is defined like this:





template�<class�_TDerived,
����������class�_TBaseInterface,
����������const�IID*�ptTypedCollectionIID,
����������class�_TContainer,
����������class�_TContainerLock,
����������const�IID*�ptValueTypeIID,
����������class�_TInserter,
����������class�_TIEnumX,
����������const�IID*�ptTypedEnumerationIID,
����������class�_TComparisonPred�=
��������������������TItfPtrIsEqualPred<_TContainer::value_type>,
����������class�_TValueCheckAdjust�=�TItfPtrGate<_TContainer::value_type>,
����������class�_TInsertRemoveNotifier�=
��������������������TInsertRemoveNotifier<_TContainer::value_type>,
����������class�_TIndexLocator�=�TIndexLocator<_TContainer>�>
struct�SItfPtrTyper
{
����typedef�_TDerived�����������������������������TDerived;
����typedef�_TBaseInterface�����������������������TBaseInterface;
����typedef�TIdToType<ptTypedCollectionIID>�������TTypedCollectionIID;
����typedef�_TContainer���������������������������TContainer;
����typedef�_TContainerLock�����������������������TContainerLock;
����typedef�_TInserter����������������������������TInserter;
����typedef�_TIEnumX������������������������������TIEnumX;
����typedef�TIdToType<ptTypedEnumerationIID>������TTypedEnumerationIID;
����typedef�_TComparisonPred����������������������TComparisonPred;
����typedef�_TValueCheckAdjust��������������������TValueCheckAdjust;
����typedef�TRefCounter<_TContainer::value_type>��TCopier;
����typedef�TItfPtrVariantVert<_TContainer::value_type,�ptValueTypeIID>
��������������������������������������������������TValueVariantConverter;
����typedef�_TContainer::value_type���������������TExternalType;
����typedef�TIdentaVert<_TContainer::value_type,��TExternalType,�TCopier>
��������������������������������������������������TValueExternalConverter;
����typedef�_TInsertRemoveNotifier����������������TInsertRemoveNotifier;
����typedef�TItfPtrStreamLoader<TContainer::value_type,�ptValueTypeIID>
��������������������������������������������������TValuePersist;
����typedef�_TIndexLocator������������������������TIndexLocator;
};



Now let's look at the meaning of each parameterized type.




  • TDerived. This is the type of class derived from DICollectionImpl—that is, the class you are implementing.


  • TBaseInterface. This is the collection interface that your component supports. This interface should be defined in an IDL file and derived from DICollection. The usual choice is to wrap this interface in ATL's IDispatchImpl, which will provide an implementation for the IDispatch portions of the interface. The content of this interface is discussed later in this section.


  • TypedCollectionIID. This is the IID of the interface in the previous parameter.


  • TContainer. This is the type of your STL-compliant container class.


  • TContainerLock. This is a functional that will be called whenever CSB needs to access your container. This functional is informed of whether read or write access is needed. When using the functional described below, TMRWSLocker, no code is generated for objects in which only one single thread can be active at a time.


  • TInserter. This is a trait called in order to insert a new element into your container.


  • TIEnumX. This is the IEnumXXXX interface that you want to support. It should be defined in an IDL file. The content of this interface is described later in this section.


  • TypedEnumerationIID. This is the IID of the interface in the previous parameter.


  • TComparisonPred. This is the predicate consulted when equality of two elements of your container's value type needs to be assessed.


  • TValueCheckAdjust. All external values that are passed into the COM+ object for any purpose (such as insertion into the container, comparison, or removal) are passed through this functional after being converted to the type internally held by the container. The functional can pass the value through unchanged, alter the value in some way before passing it on, or throw an exception to indicate that the value is not suitable in some way for processing in conjunction with your container. If your container holds an enumerated type, for example, this trait can ensure that all values passed to you fall within the enumeration range. If you hold interface pointers, you might want to ensure that no NULL pointers are passed in. (The SItfPtrTyper chooses a functional that performs this task by default.)


  • TCopier. This functional is responsible for acquiring an element of your container's value type in a way that makes it legal for the container to hold onto this value across method invocations. For interface pointers, this typically means calling AddRef, BSTRs need to be copied, and so on. The functional also is called in order to destroy an element when the container no longer references the element. Note that removing this responsibility from your value type and placing the burden on this trait makes it possible to build very efficient containers. Some STL containers tend to do a lot of copy constructing and moving of the values they hold. Therefore, forcing the internal type to maintain integrity over its value could be wasteful.


  • TValueVariantConverter. Here we perform the task of converting instances of your container's internal value type to and from the VARIANT type. This permits your container to implement the methods defined by the DICollection interface. As was the case with the copier, any value emerging from this functional must be safe to hold onto across method invocations. The functional also provides the means of destroying an instance of a VARIANT that it has produced.


  • TExternalType. This SGenericTyper parameter does not identify a trait, but the type that you would like to support in your collection's and enumerator's type-specific or type-safe (in other words, non-VARIANT) interface methods. Usually this simply is your container's value type, but it is useful to be able to specify otherwise if that value type can or should not be described in IDL. For example, if you want to use an externally keyed container such as a map and compute the key on the basis of the value, exposing the value type instead of the type of the entire key/value pair might be exactly what you want.


  • TValueExternalConverter. This trait performs the task of converting between your container's value type (for a map, this would be the type of the key/value pair) and the external type just specified. As in the case of the TValueVariantConverter, any value emerging from this trait must be safe to hold onto across method invocations. In the case where external and value type are the same, we deduce that this functional performs the same task as the copier. The trait also must support destroying an instance of the external type.


  • TInsertRemoveNotifier. This trait is informed whenever an element is inserted into or removed from your container. Notice that insertions do not necessarily pass through the TInserter. Taking control over what happens at insertion and removal time prevents you from having to override many interface methods that CSB implements for you and C++ methods that the STL container supports.


  • TValuePersist. The CSB persistence machinery calls this trait to save an instance of your container's value type into or restore it from an IStream.


  • TIndexLocator. The functional is called in order to map an ordinal index value onto an STL iterator positioned at the element corresponding to the index value in your container. The trait also informs the caller whether it can accept negative index values to allow mapping from the container's end instead of the beginning.





Predefined Traits



The functionals described next have been predefined for your use with DICollectionImpl and its Typer.



TMRSWLocker



The trait employs a single-read, multi-write lock for synchronization of container access if a thread model that allows multiple threads to execute concurrently in the COM+ object is specified. TMRSWLocker avoids construction of this lock and thus the temporary consumption of its resources if the trait is instantiated only as a temporary one, which commonly occurs during the COM+ wrapper's creation.





template�<class�TThreadModel>
class�TMRSWLocker
{
//�Types
public:
����typedef�TMRSWLock<TThreadModel>�t_Lock;

//�Construction�-�destruction
����TMRSWLocker();
����TMRSWLocker(const�TMRSWLocker&);
����~TMRSWLocker();

//�Retrieval�of�underlying�lock
����inline�operator�t_Lock&�()�const;

//�Implementation
private:
����BYTE������������m_nLockStorage[sizeof(t_Lock)];
����t_Lock*�const���m_pcMRSWLock;
};



TSequenceInserter and TSortedAssociativeInserter



These are inserter traits for sequence containers and sorted associative containers, respectively.





template�<class�TContainer>
class�TSequenceInserter
{
public:
����void�operator()�(TContainer&�rcContainer,
���������������������const�TContainer::value_type&�rValue)�const
����{
��������rcContainer.insert(rcContainer.end(),�rValue);
����}
};

template�<class�TContainer>
class�TSortedAssociativeInserter
{
public:
����void�operator()�(TContainer&�rcContainer,
���������������������const�TContainer::value_type&�rValue)�const
����{
��������if�(rcContainer.insert(rValue).second�==�false)
������������CCSBStdException::CSBThrowException(
����������������CSBEXTERNAL_E_INSERTIONFAILED);
����}
};



TValueGate



This TValueCheckAdjust template permits all values of its parameterized type to cross.





template�<class�TValueType>
class�TValueGate
{
public:
����TValueType&�operator�()�(TValueType&�rVal)�const
����{
��������return�rVal;
����}
};



TValueGate<BSTR>



This specialization of the previous template converts any NULL BSTR to a blank string ("").





template�<>
class�TValueGate<BSTR>
{
public:
����BSTR�operator�()�(BSTR�sInputString)�const
����{
��������if�(sInputString)
������������return�sInputString;

��������BSTR�sResult;
��������if�(!�(sResult�=�SysAllocString(OLESTR(""))))
������������CCSBStdException::CSBThrowException(E_OUTOFMEMORY);

��������return�sResult;
����}
};



TitfPtrGate



This TValueCheckAdjust trait for interface pointers fails any NULL interface pointers that attempt to cross. Note that while CSB does support NULL in containers of interface pointers, you might not want to do so.





template�<class�TInterface>
class�TItfPtrGate
{
public:
����TInterface�operator�()�(TInterface�piUnk)�const
����{
��������if�(!�piUnk)
������������CCSBStdException::CSBThrowException(E_POINTER);

��������return�piUnk;
����}
};



TCopier



This copier trait template simply passes through the instance of its parameterized type by value. Its Destroy method does nothing.





template�<class�TValueType>
class�TCopier
{
public:
����static�TValueType�Acquire(const�TValueType&�rSrc)
����{
��������return�rSrc;
����}
����static�void�Destroy(TValueType&)�{}
};



TCopier<VARIANT>



This specialization performs copying and cleanup for collections of Variants.





template�<>
class�TCopier<VARIANT>
{
public:
����static�VARIANT�Acquire(const�VARIANT&�rtSrc)
����{
��������return�_variant_t(rtSrc).Detach();
����}
����static�void�Destroy(VARIANT&�rtVariant)
����{
��������_variant_t(rtVariant,�false);
����}
};



TCopier<BSTR>



This trait provides value acquisition and destruction for collections of BSTRs.





template�<>
class�TCopier<BSTR>
{
public:
����static�BSTR�Acquire(LPCOLESTR�rsSrc)
����{
��������return�CComBSTR(rsSrc).Detach();
����}
����static�void�Destroy(BSTR�rsString)
����{
��������CComBSTR�cDestroyer;
��������cDestroyer.Attach(rsString);
����}
};



TRefCounter



A copier trait, this functional performs reference counting on interface pointers.





template�<class�TInterface>
class�TRefCounter
{
public:
����static�TInterface�Acquire(TInterface�piUnkSrc)
����{
��������if�(piUnkSrc)
������������piUnkSrc->AddRef();

��������return�piUnkSrc;
����}
����static�void�Destroy(TInterface�piUnk)
����{
��������if�(piUnk)
������������piUnk->Release();
����}
};



TIdentaVert



A TValueExternalConverter, this element trait converts between internal value types and external types in cases where these two types are the same.





template�<class�TValueType,�class�TExternalType,�class�TCopier>
class�TIdentaVert
{
public:
����TIdentaVert()�{}
����TIdentaVert(const�TCopier&�rcCopier)�:�m_cCopier�(rcCopier)�{}

����TExternalType�Ext(const�TValueType&�rSrc)
����{
��������return�m_cCopier.Acquire(rSrc);
����}
����TValueType�Val(const�TExternalType&�rSrc)
����{
��������return�m_cCopier.Acquire(rSrc);
����}
����void�Destroy(TExternalType&�rTarget)
����{
��������m_cCopier.Destroy(rTarget);
����}

private:
����TCopier�m_cCopier;
};



TCanonicalVariantVert



This template functional performs the role of a TValueVariantConverter. It uses _variant_t to perform the actual conversion. If that type does not exist on your platform, you might want to implement portions of it yourself or use another trait.





template�<class�TValueType>
class�TCanonicalVariantVert
{
public:
����static�VARIANT������Ext(const�TValueType&�rValue)
����{
��������return�_variant_t(rValue).Detach();
����}
����static�TValueType�Val(const�VARIANT&�rtVariant)
����{
��������//�We'll�let�the�compiler�COM�support�class�do�the�dirty�work�for�us
��������return�_variant_t(rtVariant);
����}
����static�void����������Destroy(VARIANT&�rtVariant)
����{
��������TCopier<VARIANT>::Destroy(rtVariant);
����}
};



TCanonicalVariantVert<BSTR>



This specialization handles the case where your internal value type is BSTR. This is necessary because _variant_t does not handle this case correctly in the version that ships with Visual C++ 6.0.





template�<>
class�TCanonicalVariantVert<BSTR>
{
public:
����static�VARIANT������Ext(BSTR�sValue)
����{
��������if�(!�sValue)
��������{
������������//�_variant_t�does�not�handle�this�case�correctly
������������return�_variant_t().Detach();
��������}

��������return�_variant_t(sValue).Detach();
����}
����static�BSTR����������Val(const�VARIANT&�rtVariant)
����{
��������VARIANT�tVariant;
��������VariantInit(&tVariant);
��������HRESULT�hResult�=�VariantChangeType(&tVariant,
��������������������������const_cast�<VARIANT*>�(&rtVariant),�0,�VT_BSTR);

��������if�(FAILED(hResult))
������������CCSBStdException::CSBThrowException(hResult);

��������return�V_BSTR(&tVariant);
����}
����static�void����������Destroy(VARIANT&�rtVariant)
����{
��������TCopier<VARIANT>::Destroy(rtVariant);
����}
};



TItfPtrVariantVert



A TValueVariantConverter template, this trait performs conversion of the parameterized interface pointer type to and from VARIANT.





template�<class�TInterface,�const�IID*�ptIID>
class�TItfPtrVariantVert
{
public:
����static�VARIANT������Ext(TInterface�piValue)
����{
��������return�_variant_t(piValue,�piValue�!=�NULL).Detach();
����}
����static�TInterface�Val(const�VARIANT&�rtVariant)
����{
��������_variant_t�cVariant(rtVariant);
��������cVariant.ChangeType(VT_UNKNOWN);

��������if�(!�V_UNKNOWN(&cVariant))
������������return�NULL;

��������TInterface�piResult;
��������HRESULT�hResult;

��������if�(FAILED(hResult�=�V_UNKNOWN(&cVariant)->QueryInterface(*ptIID,
�����������������������������reinterpret_cast�<void**>�(&piResult))))
������������CCSBStdException::CSBThrowException(hResult);

��������return�piResult;
����}
����static�void����������Destroy(VARIANT&�rtVariant)
����{
��������TCopier<VARIANT>::Destroy(rtVariant);
����}
};



TInsertRemoveNotifier



This default trait performs no work when notified, and no code should be generated as a result of its use in release configurations of your project.





template�<class�value_type>
class�TInsertRemoveNotifier
{
public:
����void�operator()(const�value_type&�rcValue,�bool�bInsert)�const�{}
};



TStreamOpRouter



This trait routes IStream serialization requests to the operators >> and <<, defined by CSB or you for the value type of your container.





template�<class�value_type>
class�TStreamOpRouter
{
public:
����static�IStream&�Load(IStream&�riStream,�value_type&�rTarget)
����{
��������return�riStream�>>�rTarget;
����}
����static�IStream&�Save(IStream&�riStream,�const�value_type&�rSource)
����{
��������return�riStream�<<�rSource;
����}
};



TStreamOpRouter<BSTR>



This specialization handles persistence of the BSTR type to make persistence of collections of BSTRs simple.





template�<>
class�TStreamOpRouter<BSTR>
{
public:
����static�IStream&�Load(IStream&�riStream,�BSTR&�rsTarget)
����{
��������rsTarget�=�NULL;

��������CComBSTR�siTarget;
��������riStream�>>�siTarget;

��������rsTarget�=�siTarget.Detach();

��������return�riStream;
����}
����static�IStream&�Save(IStream&�riStream,�BSTR�rsSource)
����{
��������return�riStream�<<�CComBSTR(rsSource);
����}
};



TItfPtrStreamLoader



This trait saves COM objects to or loads them from IStream. TItfPtrStreamLoader supports NULL pointers.





template�<class�TInterfacePtr,�const�IID*�ptIID>
class�TItfPtrStreamLoader
{
public:
����static�IStream&�Load(IStream&�rcStream,�TInterfacePtr&�rpiTarget,
�������������������������REFIID�riid�=�*ptIID);
����inline�static�IStream&�Save(IStream&�rcStream,�TInterfacePtr�piTarget);
};



TIndexLocator



This templated trait implements an algorithm, which maps an ordinal index value to the position of an element in your container. The algorithm varies greatly in efficiency, depending on what category of iterator your container supports. A function in the trait also tells the caller whether reverse searching is supported, again based on the type of your container's iterator.





template�<class�TContainer>
class�TIndexLocator
{
public:
����typedef�TContainer::iterator�t_Iterator;��//�compiler�bug�workaround

����inline�t_Iterator�operator()�(TContainer&�rcContainer,
����������������������������������BOOL�nIndex)�const;
����inline�static�bool�CanSearchFromEnd(const�TContainer&�rcContainer);

private:
����//�For�input�iterator�only�capable�containers
����static�t_Iterator�Locate(TContainer&�rcContainer,�BOOL�nIndex,
�����������������������������std::input_iterator_tag);
����inline�static�bool�CanSearchFromEnd(std::input_iterator_tag);

����//�For�bidirectional�iterator�capable�containers
����static�t_Iterator�Locate(TContainer&�rcContainer,�BOOL�nIndex,
�����������������������������std::bidirectional_iterator_tag);
����inline�static�bool�CanSearchFromEnd(std::bidirectional_iterator_tag);

����//�For�random�access�iterator�capable�containers
����static�t_Iterator�Locate(TContainer&�rcContainer,�BOOL�nIndex,
�����������������������������std::random_access_iterator_tag);
};



TItfPtrIsEqualPred



This TComparisonPred predicate determines equality of two objects by calling the IsEqual method of the collected interface type on one object and passing the other. The values also are said to be equal if both are NULL.





template�<class�value_type>
class�TItfPtrIsEqualPred�:�public�std::binary_function<value_type,
�������������������������������������������������������value_type,
�������������������������������������������������������bool>
{
public:
����bool�operator()(value_type�piFirst,�value_type�piSecond)�const
����{
��������if�(!�piFirst�&&�!�piSecond)
������������return�true;

��������if�(!�piFirst�||�!�piSecond)
������������return�false;

��������VARIANT_BOOL�bResult;
��������HRESULT�hResult�=�piFirst->IsEqual(piSecond,�&bResult);

��������if�(FAILED(hResult))
������������CCSBStdException::CSBThrowException(hResult);

��������return�bResult�==�VARIANT_TRUE�?�true�:�false;
����}
};



TObjInstanceComparatorPred



Another TComparisonPred for interface pointers, TObjInstanceComparatorPred uses the notion of identity to determine equality—that is, two pointers are declared equal if they point to the same COM object.





template�<class�value_type>
class�TObjInstanceComparatorPred�:�public�std::binary_function<value_type,
���������������������������������������������������������������value_type,
���������������������������������������������������������������bool>
{
public:
����bool�operator()(value_type�piFirst,�value_type�piSecond)�const
����{
��������return�CComPtr<IUnknown>(piFirst).IsEqualObject(piSecond);
����}
};



Predefined Interface Method Implementations



Now let's look at the templated interface method implementations that you will want to employ most often.



TAddNewImpl



This template adds a new COM object of the given CLSID to your container. It is well suited for monomorphic or polymorphic collections of interface pointers.





template�<class�TExternalType,�const�IID*�ptCollectionInterfaceId,
����������class�TCollectionType,�const�IID*�ptInterfaceId,
����������class�TInterface,�class�TInterfaceExternalConverter>
class�TAddNewImpl
{
public:
����static�HRESULT�Imp(TCollectionType&�rcCollection,
�����������������������REFCLSID�rClsid,�TExternalType&�rNewElement,
�����������������������TInterfaceExternalConverter&
���������������������������rcInterfaceExternalConverter);
};



TChangeImpl



The template supports the Change and TypedChange methods of your object's COM+ interfaces by locating an element in the container through either index mapping or element comparison and then changing its value to the given value. Note that the key of a sorted associative container's element must not be changed in this manner, although the Visual C++ 6.0 implementation of STL fails to detect this misuse at compile time.





//�Change�template�-�can�be�used�by�derived�classes�to�implement�the
//�Change�and�TypedChange�methods;�notice�that�DICollectionImpl�does
//�provide�a�default�implementation�for�TypedChange

class�CVariantIntVert
{
public:
����int�operator()(const�VARIANT&�rtVariant)�const
����{
��������CComVariant�cVar;
��������
��������if�(FAILED(cVar.ChangeType(VT_I4,�&rtVariant)))
������������return�-1;

��������return�V_I4(&cVar);
����}
};

//�Use�the�following�functional�as�an�argument�to�the�template�when�the
//�given�argument�should�not�identify�the�element�to�be�changed�by�index
template�<class�TExternalType>
class�TNoIntVert
{
public:
����int�operator()(const�TExternalType&)�const
����{
��������return�-1;
����}
};

template�<class�TContainer,�class�TContainerLock,�class�TPredicate,
����������class�TCopier,�class�TValueConverter,�class�TValueCheckAdjust,
����������class�TIndexLocator,�class�TInsertRemoveNotifier,�class�TIID,
����������class�TExternalType�=�VARIANT,
����������class�TExternalIntConverter�=�CVariantIntVert>
class�TChangeImpl
{
public:
����static�HRESULT�Imp(TContainer&�rcContainer,
�����������������������TContainerLock&�rcContainerLock,
�����������������������TPredicate&�rcPredicate,�TCopier&�rcCopier,
�����������������������TValueConverter&�rcValueConverter,
�����������������������TValueCheckAdjust&�rcValueCheckAdjust,
�����������������������TIndexLocator&�rcIndexLocator,
�����������������������TInsertRemoveNotifier&�rcInsertRemoveNotifier,
�����������������������const�TExternalType&�rElementToChange,
�����������������������const�TExternalType&�rNewValue,�REFCLSID�rtClsid);
};



Persistence Support



In order to implement persistence based on IStream, a class can derive from the IPersistStreamImpl template, passing its own type name as the template parameter. This template makes calls to methods named PersistLoad and PersistSave on the parameterized type of its own instance. Your own class or one of its base classes can implement these methods. If your class wraps an STL container and one of your bases is the collection wrapper, making IPersistStreamImpl one of your base classes (and adding the IPersistStream interface to your COM map) is all it takes to support persistence. This is because DICollectionImpl implements PersistLoad and PersistSave methods that serialize the entire STL container. If your COM+ container class has its own state, you simply can override PersistLoad and PersistSave (even though they are not virtual), serialize your own state, and then call your DICollectionImpl base class. IPersistStreamImpl is defined as follows:





template�<class�T>
class�ATL_NO_VTABLE�IPersistStreamImpl
{
//�Construction
protected:
����IPersistStreamImpl()�:
��������m_bDirty�(FALSE)
����{
����}

//�IUnknown
public:
����STDMETHOD(QueryInterface)(REFIID�riid,�void�**�ppvObject)�=�0;
����_ATL_DEBUG_ADDREF_RELEASE_IMPL(IPersistStreamImpl)

//�IPersist
����STDMETHOD(GetClassID)(CLSID�*pClassID)
����{
��������ATLTRACE(_T("IPersistStreamImpl::GetClassID\n"));
��������_ASSERTE�(pClassID);

��������T*�pT�=�static_cast�<T*>�(this);
��������*pClassID�=�pT->GetObjectCLSID();
����������//�using�CComCoClass::GetObjectCLSID()
��������return�S_OK;
����}

//�IPersistStream
����STDMETHOD(IsDirty)()
����{
��������ATLTRACE(_T("IPersistStreamImpl::IsDirty\n"));
��������return�static_cast�<const�T*>�(this)->IsDirty()�?�S_OK�:�S_FALSE;
����}

����STDMETHOD(Load)(LPSTREAM�pStm)
����{
��������ATLTRACE(_T("IPersistStreamImpl::Load\n"));
��������_ASSERTE�(pStm);

��������T*�pT�=�static_cast�<T*>�(this);

��������try
��������{
������������pT->PersistLoad(*pStm);
��������}
��������RETURN_EXCEPTION_AS_HRESULT(pT->GetObjectCLSID(),
������������������������������������IID_IPersistStream);

��������return�S_OK;
����}

����STDMETHOD(Save)(LPSTREAM�pStm,�BOOL�fClearDirty)
����{
��������ATLTRACE(_T("IPersistStreamImpl::Save\n"));
��������_ASSERTE�(pStm);

��������T*�pT�=�static_cast�<T*>�(this);

��������try
��������{
������������pT->PersistSave(*pStm);

������������//�Clear�the�dirty�flag,�if�requested
������������if�(fClearDirty)
����������������SetDirty(false);

��������}
��������RETURN_EXCEPTION_AS_HRESULT(pT->GetObjectCLSID(),
������������������������������������IID_IPersistStream);

��������return�S_OK;
����}

����STDMETHOD(GetSizeMax)(ULARGE_INTEGER*�pcbSize)
����{
��������//�Note:�provides�default�implementation�by�saving�to�memory�and
��������//�������then�determining�exactly�how�much�was�written

��������//�Note:�derived�classes�are�encouraged�to�override�this�method�if
��������//���������-�they�can�provide�a�more�efficient�implementation
��������//���������-�this�method�is�expected�to�be�called�in�situations
��������//�����������where�efficiency�matters

��������ATLTRACE(_T("IPersistStreamImpl::GetSizeMax\n"));
��������_ASSERTE�(pcbSize);

��������T*�pT�=�static_cast�<T*>�(this);

��������//�Create�system-provided�stream�in�memory�implementation
��������CComPtr<IStream>�ciStream;
��������HRESULT�hResult;

��������if�(FAILED(hResult�=�CreateStreamOnHGlobal(NULL,�TRUE,�&ciStream)))
������������return�CSBReportError(pT->GetObjectCLSID(),�hResult);

��������//�Perform�save�operation
��������if�(FAILED(hResult�=�Save(ciStream,�FALSE)))
������������return�hResult;

��������//�Determine�how�much�was�written
��������LARGE_INTEGER�tOffset�=�{0,�0};

��������if�(FAILED(hResult�=�ciStream->Seek(tOffset,�STREAM_SEEK_END,
��������������������������������������������pcbSize)))
������������return�CSBReportError(pT->GetObjectCLSID(),�hResult);

��������return�S_OK;
����}

//�ISupportErrorInfo�-�base�class�support
����HRESULT�InterfaceSupportsErrorInfo(REFIID�riid)�const
����{
��������static�const�IID*�arr[]�=�
��������{
�������������&IID_IPersistStream,
��������};

��������for�(int�i�=�0;�i�<�sizeof(arr)�/�sizeof(arr[0]);�i++)
������������if�(InlineIsEqualGUID(*arr[i],�riid))
����������������return�S_OK;

��������return�S_FALSE;
����}

//�Auxiliary�C++�interface
����bool�IsDirty()�const
����{
��������return�m_bDirty;
����}

����void�SetDirty(bool�bDirty)
����{
��������m_bDirty�=�bDirty;
����}

//�Implementation
private:
����bool�m_bDirty;
};



The persistence of DICollectionImpl can be upgraded to type stream support easily by substituting the IPersistStream implementation shown in Chapter 7, which routes IPersistStream methods to IPersistTypeStream methods. The PersistLoad and PersistSave methods of DICollectionImpl then should be recast in terms of ITypeStream, not IStream.



STL Adapters



Having defined the COM+ interface production mechanism, let's now turn to the consumption machinery. These are the adapters that let STL-compliant software building blocks work with COM+ collection and enumerator interfaces.



Container Adapter



At the time of this public release, this adapter implements only a small subset of the STL container specification. It supports the begin and end methods, which ease the production of STL iterators from COM collection interfaces and make the adapter suitable for use in STL loop idioms.





template�<class�TCollectionItf,�class�_TComIterator>
class�TComCollection
{
//�Construction
public:
����TComCollection(TCollectionItf&�riCollection);
����TComCollection(const�TComCollection&�rcOriginal);
����TComCollection&�operator�=�(const�TComCollection&�rcOriginal);

//�Generic�container�support
����_TComIterator�begin();
����_TComIterator�end();

//�Implementation
private:
����CComPtr<TCollectionItf>�m_ciCollection;
};



Iterator Adapter



What you see in the following code is a fully STL-compliant iterator template that requires a caching policy (which I will discuss in a moment) upon instantiation. This policy in turn needs several template parameters, which we'll look at next.





//�This�template�is�used�by�the�adapter�wherever�assignable�value_type
//�values�are�returned.�The�caller�can�use�it�to�make�a�change�to�a�value
//�in�a�collection.�Note�that�the�underlying�iterator�must�have�output
//�iterator�capabilities�and�that�the�enumerator�interface�must�implement
//�the�AssignThrough�method.
template�<class�TCacheManager>
class�TComDeref
{
//�Types
public:
����typedef�TCacheManager::TEnumeratorItf�TEnumeratorItf;
����typedef�TCacheManager::TCategory�TCategory;
����typedef�TCacheManager::TCopier�TCopier;
����typedef�TCategory::value_type�value_type;

//�Construction
����TComDeref(TEnumeratorItf&�riEnumerator,�value_type&�rValue,
��������������ULONG�nCacheIndex,�TCopier&�rcCopier);

//�Operations
����inline�operator�const�value_type&�()�const;
����const�value_type&�operator�=�(const�value_type&�rNewValue);
������//�invokes�TEnumeratorItf::AssignThrough�and�syncs�adapter's�value

//�Implementation
private:
����TEnumeratorItf&�m_riEnum;
����value_type&�����m_rAdapterValue;
����const�ULONG�����m_nCacheIndex;
����TCopier&��������m_rcCopier;
};

//
//�Adapter�template
//
template�<class�TCacheManager>
class�TComIterator�:�public�TCacheManager::TCategory
{
//�Types
public:
����typedef�TCacheManager::TEnumeratorItf�TEnumeratorItf;
����typedef�TCacheManager::TCategory�T_Category;
����typedef�TCacheManager::TCopier�TCopier;
����typedef�T_Category::value_type�value_type;
����typedef�TComDeref<TCacheManager>�CComDeref;
����typedef�std::vector<value_type>�CVector;
����typedef�T_Category::distance_type�distance_type;
����typedef�TCacheManager::t_InitData�t_InitData;

//�Construction
����explicit�TComIterator(t_InitData�InitData�=
������������������������������TCacheManager::DefaultCopyValue(),
��������������������������TCopier&�rcCopier�=�TCopier());
������//�produces�singular�value

����TComIterator(const�TComIterator&�rcOriginal,
�����������������t_InitData�InitData�=�TCacheManager::DefaultCopyValue(),
�����������������TCopier*�pcCopier�=�NULL);

����TComIterator(TEnumeratorItf&�riEnumerator,�bool�bClone�=�true,
�����������������t_InitData�InitData�=�TCacheManager::DefaultInitValue(),
�����������������TCopier&�rcCopier�=�TCopier());

//�Input�iterator�support
����bool�operator�==�(const�TComIterator&�rcTarget)�const;
������//�requires�TEnumeratorItf::IsEqual,�but�the�method�is�not�called�if
������//�the�state�of�this�iterator�or�rcTarget�is�end�marker

����inline�bool�operator�!=�(const�TComIterator&�rcTarget)�const;
������//�requirements�as�for�operator�==

����inline�const�value_type&�operator�*�()�const;
������//�requires�TEnumeratorItf::Next

����inline�CComDeref�operator�*�();
������//�requires�TEnumeratorItf::Next;�for�output�iterator�support,�the
������//�caller�can�make�an�assignment�through�the�return�value,�in�which
������//�case�TEnumeratorItf::AssignThrough�is�required

����inline�TComIterator&�operator�++�();
������//�requires�TEnumeratorItf::Next

����inline�TComIterator�operator�++�(int);
������//�requirements�as�for�prefix�operator

//�Forward�iterator�support
����TComIterator&�operator�=�(const�TComIterator&�rcOriginal);

//�Bidirectional�iterator�support
����TComIterator&�operator�--�();
������//�requires�TEnumeratorItf::BackUp

����inline�TComIterator�operator�--�(int);
������//�requirements�as�for�prefix�operator

//�Random�access�iterator�support
����TComIterator&�operator�+=�(distance_type�Distance);
������//�requires�TEnumeratorItf::SkipN�or�TEnumeratorItf::BackUpN�if�<�0

����inline�TComIterator�operator�+�(distance_type�Distance)�const;
������//�requirements�as�for�operator�+=

����inline�friend�TComIterator�operator�+�(distance_type�Distance,
�������������������������������������������const�TComIterator&�rcIterator);
������//�requirements�as�for�operator�+=

����inline�TComIterator&�operator�-=�(distance_type�Distance);
������//�requires�TEnumeratorItf::BackUpN�or�TEnumeratorItf::SkipN�if�<�0

����inline�TComIterator�operator�-�(distance_type�Distance)�const;
������//�requirements�as�for�operator�-=

����distance_type�operator�-�(const�TComIterator&�rcTarget)�const;
������//�requires�TEnumeratorItf::Compare

����inline�const�value_type&�operator�[]�(distance_type�Distance)�const;
������//�requires�TEnumeratorItf::AtIndex

����inline�CComDeref�operator�[]�(distance_type�Distance);
������//�requires�TEnumeratorItf::AtIndex;�for�output�iterator�support,�the
������//�caller�can�make�an�assignment�through�the�return�value,�in�which
������//�case�TEnumeratorItf::AssignThrough�is�required

����bool�operator�<�(const�TComIterator&�rcTarget)�const;
������//�requires�TEnumeratorItf::LessThan

����bool�operator�>�(const�TComIterator&�rcTarget)�const;
������//�requires�TEnumeratorItf::GreaterThan

����inline�bool�operator�<=�(const�TComIterator&�rcTarget)�const;
������//�requirements�as�for�operator�>

����inline�bool�operator�>=�(const�TComIterator&�rcTarget)�const;
������//�requirements�as�for�operator�<

//�Auxiliary�public�interface
����inline�bool�IsPastTheEnd()�const;
������//�calling�this�method�is�much�more�efficient�than�having�the
������//�container�adapter�build�an�iterator�positioned�past�the�end,�since
������//�this�will�involve�one�or�more�round�trips�between�the�caller�and
������//�the�(possibly�remote)�container�object

//�Implementation
private:
����TCacheManager�m_cCacheManager;

����value_type&�AtIndex(distance_type�Distance,
������������������������bool*�pbFromCache�=�NULL)�const;
����inline�void�CacheUp(bool�bMustMoveEnumerator);
����inline�CVector::difference_type�EnumIndex()�const;
����inline�TEnumeratorItf&�GetEnumerator()�const;
����inline�const�CVector&�GetCache()�const;
����inline�const�CVector::const_iterator&�GetCachePos()�const;
����inline�CVector::iterator&�GetCachePos();
����inline�const�value_type&�GetCurrentValue()�const;
����inline�value_type&�GetCurrentValue();
����inline�CVector::size_type�DesiredCacheSize()�const;
����inline�void�RepositionedEnumerator(distance_type�nByDistance);
����inline�bool&�AtIndexValueStatus()�const;
����inline�value_type&�AtIndexValue()�const;
};



TEnumeratorItf. This parameter is the interface type of the COM IEnumXXXX interface that this adapter is being hooked up to.



TCategory. This parameter represents the STL iterator category. Pick one of the typedefs from the SCat template. When instantiating SCat for this purpose, provide a type compatible with the enumerator interface's element type in the TExternalType parameter of SCat.





//�Choose�one�of�the�following�types�for�the�category�of�your�iterator's
//�cache�manager
template�<class�TExternalType,�class�TDistance�=�ptrdiff_t>
struct�SCat
{
����typedef�std::iterator<std::input_iterator_tag,�TExternalType,
��������������������������TDistance>
������������t_INPIT;
����typedef�std::iterator<std::output_iterator_tag,�TExternalType,
��������������������������TDistance>
������������t_OUTIT;
����typedef�std::iterator<std::forward_iterator_tag,�TExternalType,
��������������������������TDistance>
������������t_FWDIT;
����typedef�std::_Bidit<TExternalType,�TDistance>�t_BDRIT;
����typedef�std::_Ranit<TExternalType,�TDistance>�t_RAIT;
};



TCopier. This functional has the same role as the TCopier of DICollectionImpl; it is used to acquire and destroy values of TExternalType, with which SCat was instantiated. Often you will be able to use one of the copier traits of DICollectionImpl for this type. Since this functional is in charge of destroying elements retrieved from the enumerator (releasing them, deallocating them, or whatever is appropriate for the value type), you should never attempt to do so yourself. Of course, that is unless you elect to hold onto such values for an extended period of time while balancing acquisition/destruction operations.



In addition to supporting all required functions for all STL iterator categories, the iterator adapter implements the IsPastTheEnd function. This function can be called to determine whether the iterator currently is positioned past the end of the container to which it refers. An alternative to the STL loop idiom of comparison to a container generated past the end iterator value, this method can be considerably more efficient than the idiom.



Cache Policies



The cache policy determines how the iterator adapter interacts with the enumerator object and how different iterator objects relate to one another. Let's look at the possible cache policies now.



TPrivateCacheManager





//�Private�cache�of�fixed�given�size
template�<class�_TEnumeratorItf,�class�_TCategory,�class�_TCopier>
class�TPrivateCacheManager
{
//�Types
public:
����typedef�_TEnumeratorItf�TEnumeratorItf;
����typedef�_TCategory�TCategory;
����typedef�_TCopier�TCopier;
����typedef�TCategory::value_type�value_type;
����typedef�TCategory::distance_type�distance_type;
����typedef�std::vector<value_type>�CVector;
����typedef�CVector::size_type�t_InitData;

//�Construction�-�destruction
����TPrivateCacheManager(t_InitData�nCacheSize,�TCopier&�rcCopier);
����TPrivateCacheManager(const�TPrivateCacheManager&);��//�not�impl
����TPrivateCacheManager(const�TPrivateCacheManager&�rcOriginal,
�������������������������t_InitData�nCacheSize,�TCopier*�pcCopier);
����TPrivateCacheManager&�operator�=�(const�TPrivateCacheManager&
��������������������������������������rcOriginal);
����~TPrivateCacheManager();

//�Initialization�operations
����inline�static�t_InitData�DefaultInitValue();
����inline�static�t_InitData�DefaultCopyValue();

//�Cache�management�operations
����inline�bool�IsPastTheEnd()�const;
����void�CacheUp(bool�bMustMoveEnumerator);
����inline�CVector::difference_type�EnumIndex()�const;
����inline�TEnumeratorItf*�GetEnumerator()�const;
����inline�void�SetEnumerator(TEnumeratorItf&�riEnumerator);
����inline�static�bool�AcceptsClonedEnumerator();
����inline�const�CVector&�GetCache()�const;
����inline�const�CVector::const_iterator&�GetCachePos()�const;
����inline�CVector::iterator&�GetCachePos();
����inline�const�value_type&�GetCurrentValue()�const;
����inline�value_type&�GetCurrentValue();
����inline�CVector::size_type�DesiredCacheSize()�const;
����inline�void�RepositionedEnumerator(distance_type);
����inline�bool*�AtIndexValueStatus()�const;
����inline�value_type*�AtIndexValue()�const;

����TCopier������������������������m_cCopier;

//�Implementation
private:
����void�ClearVector();
����void�Copy(const�TPrivateCacheManager&�rcOriginal);
����inline�CVector::difference_type�Index()�const;

����TEnumeratorItf*�������������m_piEnum;
����CVector::difference_type����m_nCacheSize;
����CVector���������������������m_cLastRetrievedValues;
����CVector::iterator�����������m_cPositionInVector;
����CVector::difference_type����m_nIntervalOffset;
����mutable�value_type����������m_AtIndexValue;
����mutable�bool����������������m_bHasAtIndexValue;
};



This template implements a very efficient, general-purpose cache policy. Each iterator of this type maintains its own private cache of n elements, where n optionally can be set during iterator construction. If n is not set, it defaults to 5 for a new iterator or to the n of the original for a copied iterator. Elements are retrieved in groups of n from the enumerator. When iterating forward, the caching pattern is optimized for continued forward iteration; when iterating in reverse, it is optimized for continued reverse iteration.


Because the size of the cache is not an attribute of the type but rather a parameter to each iterator instance, iterators with different cache sizes remain type compatible with one another. They therefore can be compared, set equal to one another, and so on.



You need to recognize that the cache size will have a large impact on performance: making the cache too small will lead to numerous round trips, while making the cache too large results in a waste of space and time when caches have to be copied among iterator instances. This is likely to become an issue with a cache that is too large, since, being pointer abstractions, iterators commonly are passed by value.


Note that while this cache policy is an excellent choice in perhaps the majority of circumstances, it is unsuitable for a certain class of algorithms: those that expect to make a change to the container through one iterator instance and have that change reflected instantly through all other iterators. Remember that each iterator maintains its own private cache, and while an assignment through an output iterator immediately will be reflected in the collection, it will not propagate to other caches. In situations where this is an issue, you should choose a different cache policy.



TZeroCacheManager





//�No�state�is�stored�in�the�iterator
template�<class�_TEnumeratorItf,�class�_TCategory,�class�_TCopier>
class�TZeroCacheManager
{
//�Types
public:
����typedef�_TEnumeratorItf�TEnumeratorItf;
����typedef�_TCategory�TCategory;
����typedef�_TCopier�TCopier;
����typedef�TCategory::value_type�value_type;
����typedef�TCategory::distance_type�distance_type;
����typedef�std::vector<value_type>�CVector;
����enum�t_InitData�{�NONE�};

//�Construction�-�destruction
����TZeroCacheManager(t_InitData,�TCopier&�rcCopier);
����TZeroCacheManager(const�TZeroCacheManager&);��//�not�impl
����TZeroCacheManager(const�TZeroCacheManager&�rcOriginal,
����������������������t_InitData,�TCopier*�pcCopier);
����TZeroCacheManager&�operator�=�(const�TZeroCacheManager&�rcOriginal);
����~TZeroCacheManager();

//�Initialization�operations
����inline�static�t_InitData�DefaultInitValue();
����inline�static�t_InitData�DefaultCopyValue();

//�Cache�management�operations
����bool�IsPastTheEnd()�const;
����void�CacheUp(bool�bMustMoveEnumerator);
����inline�CVector::difference_type�EnumIndex()�const;
����inline�TEnumeratorItf*�GetEnumerator()�const;
����inline�void�SetEnumerator(TEnumeratorItf&�riEnumerator);
����inline�static�bool�AcceptsClonedEnumerator();
����inline�const�CVector&�GetCache()�const;
����inline�const�CVector::const_iterator&�GetCachePos()�const;
����inline�CVector::iterator&�GetCachePos();
����const�value_type&�GetCurrentValue()�const;
����inline�value_type&�GetCurrentValue();
����inline�CVector::size_type�DesiredCacheSize()�const;
����inline�void�RepositionedEnumerator(distance_type);
����inline�bool*�AtIndexValueStatus()�const;
����inline�value_type*�AtIndexValue()�const;

����TCopier����������������������m_cCopier;

//�Implementation
private:
����void�ClearVector()�const;
����void�Copy(const�TZeroCacheManager&�rcOriginal);
����bool�Retrieve()�const;

����TEnumeratorItf*��������������m_piEnum;
����mutable�CVector��������������m_cLastRetrievedValue;
����mutable�CVector::iterator����m_cPositionInVector;
����mutable�value_type�����������m_AtIndexValue;
����mutable�bool�����������������m_bHasAtIndexValue;
����mutable�enum�{�CL_FALSE,�CL_TRUE,�CL_UNKNOWN�}�m_eCurrentIsLast;
};



All caching is disabled under this policy. For example, whenever an iterator's operator * is accessed, the value corresponding to the current position is retrieved anew from the collection.



Since the COM+ enumeration idiom does not support continued retrieval of the same value, this policy needs nonstandard IEnumXXXX support from the enumerator object. While such support is easy to enable for your own containers wrapped using DICollectionImpl, it is not likely to exist with third-party collections. You therefore will not be able to hook up an iterator directly to such a collection using this cache policy.



TFullCentralizedCacheManager





//�Shared�cache�of�entire�container
template�<class�TEnumeratorItf,�class�value_type,�class�TCopier>
class�TSharedCache;

template�<class�_TEnumeratorItf,�class�_TCategory,�class�_TCopier>
class�TFullCentralizedCacheManager
{
//�Types
public:
����typedef�_TEnumeratorItf�TEnumeratorItf;
����typedef�_TCategory�TCategory;
����typedef�_TCopier�TCopier;
����typedef�TCategory::value_type�value_type;
����typedef�TCategory::distance_type�distance_type;
����typedef�std::vector<value_type>�CVector;
����enum�t_InitData�{�NONE�};

//�Construction�-�destruction
����TFullCentralizedCacheManager(t_InitData,�TCopier&�rcCopier);
����TFullCentralizedCacheManager(const�TFullCentralizedCacheManager&);
������//�not�impl
����TFullCentralizedCacheManager(const�TFullCentralizedCacheManager&
���������������������������������rcOriginal,�t_InitData,
���������������������������������TCopier*�pcCopier);
����TFullCentralizedCacheManager&�operator�=�(
��������const�TFullCentralizedCacheManager&�rcOriginal);
����~TFullCentralizedCacheManager();

//�Initialization�operations
����inline�static�t_InitData�DefaultInitValue();
����inline�static�t_InitData�DefaultCopyValue();

//�Cache�management�operations
����inline�bool�IsPastTheEnd()�const;
����inline�void�CacheUp(bool�bMustMoveEnumerator);
����inline�CVector::difference_type�EnumIndex()�const;
����inline�TEnumeratorItf*�GetEnumerator()�const;
����inline�void�SetEnumerator(TEnumeratorItf&�riEnumerator);
����inline�static�bool�AcceptsClonedEnumerator();
����inline�const�CVector&�GetCache()�const;
����inline�const�CVector::const_iterator&�GetCachePos()�const;
����inline�CVector::iterator&�GetCachePos();
����inline�const�value_type&�GetCurrentValue()�const;
����inline�value_type&�GetCurrentValue();
����inline�CVector::size_type�DesiredCacheSize()�const;
����inline�void�RepositionedEnumerator(distance_type�nByDistance);
����inline�bool*�AtIndexValueStatus()�const;
����inline�value_type*�AtIndexValue()�const;

����TCopier��������������m_cCopier;

//�Implementation
private:
����typedef�TSharedCache<TEnumeratorItf,�value_type,�TCopier>�CCache;

����void�Copy(const�TFullCentralizedCacheManager&�rcOriginal);

����CVector::iterator����m_cPositionInVector;
����CCache*��������������m_pcSharedCache;
};



All iterators that are split off from one common root iterator share the same cache under this policy. The cache spans the entire size of the collection. The cache is discarded once the last iterator that uses it is destroyed. All input operations are satisfied from the cache; the enumerator is never accessed on input. All output operations are immediately committed to the enumerator.



An iterator is said to have been split off from a root iterator if it is either copy-constructed or returned from a method of the root iterator or another iterator that has been split off in this manner.



Support Structures



CSB uses several devices and strategies to support its operation. Let's examine them now.



MRSW Lock



The multi-read, single-write lock is a synchronization object built on top of Win32 synchronization primitives commonly supplied in COM environments on any platform. An arbitrary number of threads can enter this lock under its AcquireReadLock method, but only one thread can enter it via AcquireWriteLock. A write lock cannot be acquired while a read lock is active, and vice versa. The lock supports conversion of read locks to write locks, and vice versa. You can find the precise operational characteristics of the lock described in its header file. The MRSW lock used by CSB is reproduced and analyzed in Chapter 4 of this book.



Since most STL library vendors define thread safety for their product in terms of its capability to withstand concurrent read but not concurrent write, the MRSW lock is employed by CSB throughout to protect STL-compliant containers against access exceeding that specification. This protection extends to concurrent access by multiple COM+ objects, such as those in the DICollectionImpl and IPersistStreamImpl hierarchy and various enumerator object instances.



Macros



CSB uses a number of macros and other small support scaffolding internally. You might find some of these independently useful. The file AtlSupport.h, for example, contains COM map macros that can be used in base classes or templates, which are not derived from ATL's CComObjectRootEx class. Derived classes then can map chain (COM_INTERFACE_ENTRY_CHAIN) to such base classes or templates. These particular mechanisms are discussed in Chapter 6 of this book.


There is also the stack lock template CCSBLock, which calls the methods Lock and Unlock on an instance of its parameterized type when explicitly requested or during construction and destruction. Using this template can ensure that you don't forget to deallocate a resource upon exiting a function or block.


The IEnumCallAsResolver.h file contains a macro that will generate code for the bonding routines needed to link a proxy/stub DLL with IEnumXXXX interfaces. These interfaces contain some local methods, and MIDL requires these bonding routines for mapping each cross-apartment call_as method to the local one. The macro is defined as follows:
12





#define�IENUM_CALL_AS_RESOLVER(DataType,�IntfType)������������������������\
�/*�[local]�*/�HRESULT�STDMETHODCALLTYPE�IEnum##DataType##_Next_Proxy(����\
����IEnum##DataType�__RPC_FAR�*�This,�������������������������������������\
����/*�[in]�*/�ULONG�celt,������������������������������������������������\
����/*�[length_is][size_is][out]�*/�IntfType�*�rg##DataType,��������������\
����/*�[out]�*/�ULONG�__RPC_FAR�*pCeltFetched)����������������������������\
�{������������������������������������������������������������������������\
����ULONG�CeltFetched;����������������������������������������������������\
��������������������������������������������������������������������������\
����if�(!�This�||�!�rg##DataType)�����������������������������������������\
��������return�E_POINTER;�������������������������������������������������\
��������������������������������������������������������������������������\
����if�(!�celt)�����������������������������������������������������������\
��������return�E_INVALIDARG;����������������������������������������������\
��������������������������������������������������������������������������\
����if�(!�pCeltFetched)���������������������������������������������������\
��������if�(celt�==�1)����������������������������������������������������\
������������pCeltFetched�=�&CeltFetched;����������������������������������\
��������else��������������������������������������������������������������\
������������return�E_POINTER;���������������������������������������������\
��������������������������������������������������������������������������\
����return�IEnum##DataType##_RemoteNext_Proxy(This,�celt,�rg##DataType,���\
����������������������������������������������pCeltFetched);��������������\
�}������������������������������������������������������������������������\
��������������������������������������������������������������������������\
�/*�[call_as]�*/�HRESULT�STDMETHODCALLTYPE�IEnum##DataType##_Next_Stub(���\
����IEnum##DataType�__RPC_FAR�*�This,�������������������������������������\
����/*�[in]�*/�ULONG�celt,������������������������������������������������\
����/*�[length_is][size_is][out]�*/�IntfType�*��rg##DataType,�������������\
����/*�[out]�*/�ULONG�__RPC_FAR�*pCeltFetched)����������������������������\
�{������������������������������������������������������������������������\
����return�IEnum##DataType##_Next(This,�celt,�rg##DataType,�pCeltFetched);\
�}������������������������������������������������������������������������\
��������������������������������������������������������������������������\
�/*�[restricted][local]�*/�HRESULT�STDMETHODCALLTYPE����������������������\
����IEnum##DataType##__GetIterator_Proxy(IEnum##DataType�__RPC_FAR�*�This,\
����/*�[in]�*/�ULONG�CacheIndex,������������������������������������������\
����REFITERATOR�Iterator)�������������������������������������������������\
�{������������������������������������������������������������������������\
����return�ERC_CSBEXTERNAL_E_CANT_REMOTE;���������������������������������\
�}������������������������������������������������������������������������\
��������������������������������������������������������������������������\
�/*�[restricted][call_as]�*/�HRESULT�STDMETHODCALLTYPE��������������������\
����IEnum##DataType##__GetIterator_Stub(IEnum##DataType�__RPC_FAR�*�This)�\
�{������������������������������������������������������������������������\
����return�E_UNEXPECTED;��������������������������������������������������\
�}



Error Architecture



CSB HRESULTs, which belong to FACILITY_ITF, are further segmented into subfacilities. Compatible with the general rules for COM+ error codes, subfacilities and the error architecture allow for different areas of your system to evolve independently, eliminating the need to prevent code collisions by maintaining one giant error-code file with unique codes. Such code collisions make error code passthrough from interface to interface difficult to manage in your project. It also means that you will be able to pinpoint the general area of your software system an error originated from, just by looking at the code. Furthermore, the low word of each CSB HRESULT maps to a string resource in the CSB support library. Each code X also is accompanied by the symbol ERC_X in CSB's type library, for easy inspection of errors from various development environments.


The file CSBError.h contains a few global functions that allow CSB (and you, if you choose to use them) to return an error code from an interface in an object that supports error information for this interface. The principal difference between these reporting functions and ATL's AtlReportError is that CSB's functions can do the following:




  • Look for your error string resource in a chain of modules, not just one module


  • Format and report system-defined errors


  • Report an error condition that emanated from a secondary object, making sure that the thread error object is set up but not overwritten if the secondary component already did this





CSB also defines a C++ standard-compliant exception class, CCSBStdException. The class contains a number of static methods that will throw an object of this type and that largely correspond in functionality to the CSBReportError functions discussed earlier in this section. Exceptions of course must not be thrown across interface boundaries. But you can use the macros
RETURN_ EXCEPTION_AS_HRESULT and RETURN_EXCEPTION_AS_NOERRINFO_HRESULT in interface methods to catch exceptions of most popular exception types (including those of CSB), produce meaningful error codes from them, and return these to your caller. The former macro will set up error information during this process; the latter one will not. The principles behind this error-handling strategy and the details of its implementation are discussed in Chapter 1 of this book.



Selecting Functionality



In this section, I will discuss how to configure CSB's support for various features found in different kinds of STL containers and iterators. I will also cover how to enable areas of optional CSB functionality. How you determine the level of support depends on which side of the component equation you stand on: that of the interface producer, or that of the consumer. CSB is configured in two ways. Let's take a look at them both.



Explicit Configuraton: For Collection Wrappers



You must make two choices for configuring your container's COM interfaces: what to include in your collection's interface, and what to include in the enumerator's interface. Of course, you can add any arbitrary method to the collection interface and implement it yourself in the COM+ wrapper class, but there are certain functions that DICollectionImpl will implement for you. Note that the non-VARIANT portion—that is, the canonical interface—is not virtual in DICollectionImpl. Therefore, these methods will not even be compiled unless you include them in your collection interface. This can be useful if your type arrangement precludes an error-free compilation of any such method. It also reduces the size of your code when dealing with methods that simply do not apply to your collection. The full canonical interface of a CSB collection is as follows. (Also see TExternalType and TIEnumX, discussed earlier in this section.)





[helpstring("Add�an�item�to�the�collection")]
HRESULT�TypedAdd([in]�TExternalType�Element);
[helpstring("Request�a�new�item�from�the�collection")]
HRESULT�TypedAddNew([out,�retval]�TExternalType*�Element);
[helpstring("Change�the�given�element")]
HRESULT�TypedChange([in]�TExternalType�TargetElement,
��������������������[in]�TExternalType�NewElement);
[helpstring("Determine�whether�this�object�is�in�the�collection")]
HRESULT�TypedContains([in]�TExternalType�Element,
����������������������[out,�retval]�VARIANT_BOOL*�ReturnVal);
[helpstring("Find�an�object�like�the�given�one")]
HRESULT�TypedFind([in]�TExternalType�Element,
������������������[out,�retval]�TExternalType*�Match);
[helpstring("Retrieve�the�element�at�position�Index")]
HRESULT�TypedValue([in]�int�Index,�[out,�retval]�TExternalType*�Element);
[helpstring("Remove�the�given�item�from�the�collection")]
HRESULT�TypedRemove([in]�TExternalType�Element);
[restricted]
HRESULT�_TypedNewEnum([out]�TIEnumX**�ReturnVal);
[restricted]
HRESULT�_NewEnumNoCopy([out]�TIEnumX**�ReturnVal);



Any such interface will be derived from DICollection, which is a dual interface with the following content:





[helpstring("Add�an�item�to�the�collection")]
HRESULT�Add([in]�VARIANT�Element);
[helpstring("Request�a�new�item�from�the�collection")]
HRESULT�AddNew([in,�optional]�VARIANT�What,
���������������[out,�retval]�VARIANT*�Element);
[helpstring("Change�the�element�with�the�given�value�or�the�element�at�"
������������"the�given�index")]
HRESULT�Change([in]�VARIANT�IndexOrElement,�[in]�VARIANT�NewValue);
[helpstring("Determine�whether�this�value�occurs�in�the�collection")]
HRESULT�Contains([in]�VARIANT�Element,
�����������������[out,�retval]�VARIANT_BOOL*�ReturnVal);
[helpstring("Return�an�element�with�the�given�value")]
HRESULT�Find([in]�VARIANT�Element,�[out,�retval]�VARIANT*�Match);
[propget,�helpstring("Number�of�items�in�the�collection")]
HRESULT�Count([out,�retval]�int*�ReturnVal);
[id(DISPID_VALUE),�helpstring("Retrieve�the�element�at�position�Index")]
HRESULT�Value([in]�int�Index,�[out,�retval]�VARIANT*�Element);
[helpstring("Remove�the�item�at�Index�from�the�collection")]
HRESULT�Remove([in]�int�Index);
[helpstring("Make�a�duplicate�of�this�collection�object")]
HRESULT�Clone([out,�retval]�DICollection**�Copy);
[helpstring("Remove�all�elements�from�this�collection")]
HRESULT�Clear();
[id(DISPID_NEWENUM),�restricted]
HRESULT�_NewEnum([out,�retval]�IEnumVARIANT**�ReturnVal);



DICollectionImpl will implement most methods in DICollection as well. Note that some methods in the canonical interface and its base are not implemented by DICollectionImpl because an implementation either is not appropriate or cannot be provided in a manner completely independent of type. In such cases, E_NOTIMPL is returned or no implementation function is provided at all. Look at the predefined interface method implementations (covered earlier in this section) provided for common type categories; perhaps one of them is applicable to your type.


Note that the _NewEnum and _TypedNewEnum methods produce an enumerator on a clone of the collection, not the collection itself. This is done to avoid undetermined behavior when changes are made to the collection while enumerator objects are outstanding. The conditions under which an STL iterator (on which enumerators are based) remains valid after a change to the container are determined on a per-container basis, as I discussed earlier in the chapter in the section "A Review of STL." Making a copy and thus precluding any change that is not made through the enumerator itself is therefore a safe approach. If, however, you want to improve the performance of the enumeration or allow the enumerator to make changes to the original collection, use the _NewEnumNoCopy method.


The content of your enumerator interface will depend on your container's iterator category. A full interface for a random access iterator looks like this:





//�IEnumXXXX�portion
[local]
HRESULT�Next([in]�ULONG�celt,
�������������[out,�size_is(celt),�length_is(*pCeltFetched)]
�������������TExternalType*�Element,
�������������[out]�ULONG*�pCeltFetched);
[call_as(Next)]
HRESULT�RemoteNext([in]�ULONG�celt,
�������������������[out,�size_is(celt),�length_is(*pCeltFetched)]
�������������������TExternalType*�Element,
�������������������[out]�ULONG*�pCeltFetched);
HRESULT�Skip([in]�ULONG�celt);
HRESULT�Reset();
HRESULT�Clone([out]�TIEnumX**�ppEnum);

//�Position�comparison
HRESULT�IsEqual([in]�TIEnumX*�Target,
����������������[in]�ULONG�CacheIndex,�[in]�ULONG�TargetCacheIndex,
����������������[out,�retval]�boolean*�Result);

//�Output�support
HRESULT�AssignThrough([in]�ULONG�CacheIndex,�[in]�TExternalType�NewValue);

//�Bidirectional�support
HRESULT�BackUp([in,�out]�ULONG*�SingleHops);

//�Random�access�support
HRESULT�SkipN([in]�ULONG�Hops);
HRESULT�BackUpN([in,�out]�ULONG*�Hops);
HRESULT�Compare([in]�TIEnumX*�Target,�[in]�ULONG�CacheIndex,
����������������[in]�ULONG�TargetCacheIndex,
����������������[out,�retval]�LONG*�Result);
HRESULT�AtIndex([in]�LONG�Index,�[in]�ULONG�CacheIndex,
����������������[out,�retval]�TExternalType*�Result);
HRESULT�LessThan([in]�TIEnumX*�Target,�[in]�ULONG�CacheIndex,
�����������������[in]�ULONG�TargetCacheIndex,
�����������������[out,�retval]�boolean*�Result);
HRESULT�GreaterThan([in]�TIEnumX*�Target,�[in]�ULONG�CacheIndex,
��������������������[in]�ULONG�TargetCacheIndex,
��������������������[out,�retval]�boolean*�Result);

//�Implementation�support
cpp_quote("#ifdef�__cplusplus")
cpp_quote("extern�\"C++\"�namespace�std�{�<your�container�type�is�forward�"
����������"declared�here>�}")
cpp_quote("typedef�CSB::TTypeSafeIterator<std::<your�container�type>�>&�"
����������"REF<TExternalType>ITERATOR;")
cpp_quote("#else")
typedef�REFITERATOR�REF<TExternalType>ITERATOR;
cpp_quote("#endif")

[local,�restricted]
HRESULT�_GetIterator([in]�ULONG�CacheIndex,
���������������������REF<TExternalType>ITERATOR�Iterator);
[call_as(_GetIterator),�restricted]
HRESULT�_RemoteGetIterator();

//�Zero�cache�manager�support
HRESULT�CurrentValue([out,�retval]�TExternalType*�Value);
HRESULT�NextWithStatus([out]�TExternalType*�Value,
�����������������������[out,�retval]�boolean*�IsLast);



In your interface, you will want to include only those sections that are compatible with your iterator. If you try to enable support for which your container's iterators are not powerful enough, you will encounter compilation errors.



Implicit Configuration: For Iterator Adapters



As you will recall from the discussion of the iterator adapter's category typedef, you indicate what operations you expect the iterator to fulfill at type instantiation time. This, however, does not provide the full capability determination, since you might want to use a forward iterator as an output iterator as well. (Instead, the typedef's role in STL is to allow you to build efficient algorithms for various iterator categories and then to route a call to the most efficient algorithm possible based on the type of iterator instance passed.) Mostly, you engage one of the iterator adapter's segments of functionality by making a call to one of its methods. Because the iterator is a template, only this call forces that method and any other methods called by it to be compiled.


For example, by calling an iterator adapter's operator --, you are compiling in the adapter's bidirectional iterator support. The adapter, in turn, will compile a call to the enumerator type's bidirectional support method, BackUp. If the compiler now complains that no such method exists, the enumerator probably does not have bidirectional support, which means the iterator adapter does not either.



Project Configuration



Now let's look at some information that will be useful when configuring settings for projects that use CSB.



Header Files



CSB headers expect a number of other headers to have been included before them. These other headers are not included in the CSB headers themselves, either because some parameterized type dictates which headers are needed or because these headers are system headers that belong in your project's precompiled header. Examine the sample project's header files (for example, StringCollection.h and StdAfx.h) to see what arrangement and order of included files is appropriate.



Warnings



By default, CSB code generates a number of warnings with the Visual C++ 6.0 compiler in your own translation units. These warnings are benign and should be suppressed. Rather than attempting to perform the suppression itself, which is difficult to do in a compiler-independent manner, CSB leaves this to you. See the sample project's StdAfx.h file on the companion CD for one viable approach.



Compiler and STL Support



CSB has been compiled with the Visual C++ 6.0 compiler (with various service pack levels) on Microsoft Windows 95, Windows 98, Windows NT 4, and Windows 2000. It is known to not compile with version 5 of Visual C++. This is because the version 5 compiler contains a bug that sometimes prevents it from generating code for methods of a class template whose definitions do not occur at the place of declaration. If you want to port CSB to Visual C++ 5, you will need to account for this bug by moving some class method definitions into the class definition itself. (Be aware that any such port still is subject to all provisions of the license agreement.) At one point during its development, CSB compiled under Visual C++ 5 in this manner. I removed these workarounds when version 6 of the compiler was released, primarily for aesthetic reasons.



Take a look at Microsoft Knowledge Base article Q167733. You will find that Visual C++ does not exhibit standard C++ behavior for operator new. While that might or might not be a problem for your own code, Microsoft's own STL implementation delivered with Visual C++ will not fare well, should new ever return NULL. You might want to address this issue in your project. I have implemented an operator new that exhibits C++ standard-compliant behavior while maintaining the benefits of Visual C++'s CRT diagnostic support in debug mode.13 Note that this solution does not employ the workaround suggested in the Knowledge Base article. This is because calling _set_new_handler can have a negative impact on the EXE module or other DLLs linked dynamically to the CRT in the same process, since the new handler is set processwide.



The STL implementation that ships with Visual C++ is reported to contain certain bugs that prevent it from being used reliably in multithreaded projects. I am referring to the definition of thread safety that should allow concurrent read operations, but not concurrent write operations. Patches available at Dinkumware, Ltd.14 aim to fix those deficiencies. Consider applying these patches before employing CSB's multithread features in a production environment when compiling with Microsoft's STL.



CSB has been successfully adapted for compilation with Rogue Wave's Standard C++ Library, as well as that of Microsoft. Some minor modifications were necessary. The companion CD contains the Microsoft version.



No comments: