#if !defined(FUSION_ARRAYHELP_H_INCLUDED_) #define FUSION_ARRAYHELP_H_INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include #include #include "fusionheap.h" #include "fusiontrace.h" // // arrayhelp.h // // Helper function(s) to deal with growable arrays. // // Users of this utility should provide explicit template // specializations for classes for which you can safely (without // possibility of failure) transfer the contens from a source // instance to a destination instance, leaving the source "empty". // // If moving the data may fail, you must provide a specialization // of FusionCopyContents() which returns an appropriate HRESULT // on failure. // // // C++ note: // // the C++ syntax for explicit function template specialization // is: // // template <> BOOLEAN FusionCanMoveContents(CFoo *p) { UNUSED(p); return TRUE; } // #if !defined(FUSION_UNUSED) #define FUSION_UNUSED(x) (x) #endif class CSaveErrorInfo { public: CSaveErrorInfo() : m_pIErrorInfo(NULL) { ::GetErrorInfo(0, &m_pIErrorInfo); } ~CSaveErrorInfo() { ::SetErrorInfo(0, m_pIErrorInfo); if (m_pIErrorInfo != NULL) m_pIErrorInfo->Release(); } private: IErrorInfo *m_pIErrorInfo; }; // // Alternate CSaveErrorInfo implementation for templates which want to not require // a link dependency on oleaut32.dll. // class CSaveErrorInfoNull { public: CSaveErrorInfoNull() { } ~CSaveErrorInfoNull() { } }; // // The default implementation just does assignment which may not fail; // you can (and must if assignment may fail) specialize as you like to // do something that avoids data copies; you may assume that the source // element will be destroyed momentarily. // // // The FusionCanMemcpyContents() template function is used to determine // if a class is trivial enough that a raw byte transfer of the old // contents to the new contents is sufficient. The default is that the // assignment operator is used as that is the only safe alternative. // template inline BOOLEAN FusionCanMemcpyContents( T *ptDummyRequired = NULL ) { FUSION_UNUSED(ptDummyRequired); return FALSE; } // // The FusionCanMoveContents() template function is used by the array // copy template function to optimize for the case that it should use // FusionMoveContens(). // // When overriding this function, the general rule is that if the data // movement may allocate memory etc. that will fail, we need to use the // FusionCopyContens() member function instead. // // It takes a single parameter which is not used because a C++ template // function must take at least one parameter using the template type so // that the decorated name is unique. // template inline BOOLEAN FusionCanMoveContents( T *ptDummyRequired = NULL ) { FUSION_UNUSED(ptDummyRequired); return FALSE; } template <> inline BOOLEAN FusionCanMoveContents(LPWSTR *ptDummyRequired) { FUSION_UNUSED(ptDummyRequired); return TRUE; } // // Override FusionMoveContents to be a useful implementation which // takes the contents of rtSource and transfers them to rtDestination. // The transfer may not fail (returns VOID). The expectation is that // any value that was stored in rtSource are moved to rtDestination // and rtSource is left in a quiescent state. E.g. any pointers to // objects can be simply assigned from rtSource to rtDestination and // then set to NULL in rtSource. You may also assume that the destination // element has only had the default constructor run on it, so you // may choose to take shortcuts about not freeing non-NULL pointers // in rtDestination if you see fit. // template inline VOID FusionMoveContents( T &rtDestination, T &rtSource ) { rtDestination = rtSource; } template <> inline VOID FusionMoveContents( LPWSTR &rtDestination, LPWSTR &rtSource ) { if ( rtDestination ) FUSION_DELETE_ARRAY(rtDestination); rtDestination = rtSource; rtSource = NULL; } // // FusionCopyContents is a default implementation of the assignment // operation from rtSource to rtDestination, except that it may return a // failure status. Trivial classes which do define an assignment // operator may just use the default definition, but any copy implementations // which do anything non-trivial need to provide an explicit specialization // of FusionCopyContents for their class. // template inline HRESULT FusionCopyContents( T &rtDestination, const T &rtSource ) { rtDestination = rtSource; return NOERROR; } template inline BOOL FusionWin32CopyContents( T &rtDestination, const T &rtSource ) { rtDestination = rtSource; return TRUE; } template <> inline HRESULT FusionCopyContents( LPWSTR &rtDestination, const LPWSTR &rtSource ) { SIZE_T cch = (SIZE_T)((rtSource == NULL) ? 0 : ::wcslen(rtSource)); if ( cch == 0) { rtDestination = NULL; return S_OK; } rtDestination = new WCHAR[cch]; if ( ! rtDestination) return E_OUTOFMEMORY; memcpy(rtDestination, rtSource, (cch+1)*sizeof(WCHAR)); return NOERROR; } // // FusionAllocateArray() is a helper function that performs array allocation. // // It's a separate function so that users of these helpers may provide an // explicit specialization of the allocation/default construction mechanism // for an array without replacing all of FusionExpandArray(). // template inline HRESULT FusionAllocateArray( SIZE_T nElements, T *&rprgtElements ) { HRESULT hr = NOERROR; rprgtElements = NULL; T *prgtElements = NULL; if (nElements != 0) { prgtElements = new T[nElements]; if (prgtElements == NULL) { hr = E_OUTOFMEMORY; goto Exit; } } rprgtElements = prgtElements; hr = NOERROR; Exit: return hr; } template inline BOOL FusionWin32AllocateArray( SIZE_T nElements, T *&rprgtElements ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); rprgtElements = NULL; T *prgtElements = NULL; if (nElements != 0) IFALLOCFAILED_EXIT(prgtElements = new T[nElements]); rprgtElements = prgtElements; fSuccess = TRUE; Exit: return fSuccess; } template <> inline HRESULT FusionAllocateArray(SIZE_T nElements, LPWSTR *&rprgtElements) { HRESULT hr = NOERROR; SIZE_T i; rprgtElements = NULL; LPWSTR *prgtElements = NULL; if (nElements != 0) { prgtElements = new PWSTR[nElements]; if (prgtElements == NULL) { hr = E_OUTOFMEMORY; goto Exit; } } for ( i=0; i < nElements; i++) prgtElements[i] = NULL ; rprgtElements = prgtElements; hr = NOERROR; Exit: return hr; } // // FusionFreeArray() is a helper function that performs array deallocation. // // It's a separate function so that users of the array helper functions may // provide an explicit specialization of the deallocation mechanism for an // array of some particular type without replacing the whole of FusionExpandArray(). // // We include nElements in the parameters so that overridden implementations // may do something over the contents of the array before the deallocation. // The default implementation just uses operator delete[], so nElements is // unused. // template inline VOID FusionFreeArray( SIZE_T nElements, T *prgtElements ) { FUSION_UNUSED(nElements); ASSERT_NTC((nElements == 0) || (prgtElements != NULL)); if (nElements != 0) FUSION_DELETE_ARRAY(prgtElements); } template <> inline VOID FusionFreeArray(SIZE_T nElements, LPWSTR *prgtElements) { FUSION_UNUSED(nElements); ASSERT_NTC((nElements == 0) || (prgtElements != NULL)); for (SIZE_T i = 0; i < nElements; i++) prgtElements[i] = NULL ; if (nElements != 0) FUSION_DELETE_ARRAY(prgtElements); } template inline HRESULT FusionResizeArray( T *&rprgtArrayInOut, SIZE_T nOldSize, SIZE_T nNewSize ) { HRESULT hr = NOERROR; FN_TRACE_HR(hr); T *prgtTempNewArray = NULL; // // nMaxCopy is the number of elements currently in the array which // need to have their values preserved. If we're actually shrinking // the array, it's the new size; if we're expanding the array, it's // the old size. // const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize; if ((nOldSize != 0) && (rprgtArrayInOut == NULL)) { hr = E_INVALIDARG; goto Exit; } // If the resize is to the same size, complain in debug builds because // the caller should have been smarter than to call us, but don't do // any actual work. ASSERT(nOldSize != nNewSize); if (nOldSize == nNewSize) { hr = NOERROR; goto Exit; } // Allocate the new array: IFCOMFAILED_EXIT(::FusionAllocateArray(nNewSize, prgtTempNewArray)); if (::FusionCanMemcpyContents(rprgtArrayInOut)) { memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy); } else if (!::FusionCanMoveContents(rprgtArrayInOut)) { // Copy the body of the array: for (SIZE_T i=0; i inline BOOL FusionWin32ResizeArray( T *&rprgtArrayInOut, SIZE_T nOldSize, SIZE_T nNewSize ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); T *prgtTempNewArray = NULL; // // nMaxCopy is the number of elements currently in the array which // need to have their values preserved. If we're actually shrinking // the array, it's the new size; if we're expanding the array, it's // the old size. // const SIZE_T nMaxCopy = (nOldSize > nNewSize) ? nNewSize : nOldSize; PARAMETER_CHECK((rprgtArrayInOut != NULL) || (nOldSize == 0)); // If the resize is to the same size, complain in debug builds because // the caller should have been smarter than to call us, but don't do // any actual work. ASSERT(nOldSize != nNewSize); if (nOldSize != nNewSize) { // Allocate the new array: IFW32FALSE_EXIT(::FusionWin32AllocateArray(nNewSize, prgtTempNewArray)); if (::FusionCanMemcpyContents(rprgtArrayInOut)) { memcpy(prgtTempNewArray, rprgtArrayInOut, sizeof(T) * nMaxCopy); } else if (!::FusionCanMoveContents(rprgtArrayInOut)) { // Copy the body of the array: for (SIZE_T i=0; i inline BOOL FusionWin32CopyContents(Typename &rtDest, const Typename &rcSource) { \ FN_PROLOG_WIN32 IFW32FALSE_EXIT(rtDest.CopyFunc(rcSource)); FN_EPILOG } \ template<> inline HRESULT FusionCopyContents(Typename &rtDest, const Typename &rcSource) { \ HRESULT hr = E_FAIL; FN_TRACE_HR(hr); IFW32FALSE_EXIT(::FusionWin32CopyContents(rtDest, rcSource)); FN_EPILOG } \ template<> inline VOID FusionMoveContents(Typename &rtDest, Typename &rcSource) { \ FN_TRACE(); HARD_ASSERT2_ACTION(FusionMoveContents, "FusionMoveContents not allowed in 99.44% of cases."); } #endif // !defined(FUSION_ARRAYHELP_H_INCLUDED_)