|
|
#if !defined(FUSION_ARRAYHELP_H_INCLUDED_)
#define FUSION_ARRAYHELP_H_INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <windows.h>
#include <oleauto.h>
#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>(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 <typename T> 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<T>().
//
// 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 <typename T> inline BOOLEAN FusionCanMoveContents( T *ptDummyRequired = NULL ) { FUSION_UNUSED(ptDummyRequired); return FALSE; }
template <> inline BOOLEAN FusionCanMoveContents<LPWSTR>(LPWSTR *ptDummyRequired) { FUSION_UNUSED(ptDummyRequired); return TRUE; }
//
// Override FusionMoveContents<T> 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 <typename T> inline VOID FusionMoveContents( T &rtDestination, T &rtSource ) { rtDestination = rtSource; }
template <> inline VOID FusionMoveContents<LPWSTR>( 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<T> for their class.
//
template <typename T> inline HRESULT FusionCopyContents( T &rtDestination, const T &rtSource ) { rtDestination = rtSource; return NOERROR; }
template <typename T> inline BOOL FusionWin32CopyContents( T &rtDestination, const T &rtSource ) { rtDestination = rtSource; return TRUE; }
template <> inline HRESULT FusionCopyContents<LPWSTR>( 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 <typename T> 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 <typename T> 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<LPWSTR>(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 <typename T> 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<LPWSTR>(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 <typename T> 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<nMaxCopy; i++) { IFCOMFAILED_EXIT(::FusionCopyContents(prgtTempNewArray[i], rprgtArrayInOut[i])); } } else { // Move each of the elements:
for (SIZE_T i=0; i<nMaxCopy; i++) { ::FusionMoveContents(prgtTempNewArray[i], rprgtArrayInOut[i]); } }
// We're done. Blow away the old array and put the new one in its place.
::FusionFreeArray(nOldSize, rprgtArrayInOut); rprgtArrayInOut = prgtTempNewArray; prgtTempNewArray = NULL;
// Canonicalize the HRESULT we're returning so that we don't return random
// S_FALSE or other success HRESULTs from the allocator or copy functions.
hr = NOERROR;
Exit:
if (prgtTempNewArray != NULL) ::FusionFreeArray(nNewSize, prgtTempNewArray);
return hr; }
template <typename T> 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<nMaxCopy; i++) IFW32FALSE_EXIT(::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i])); } else { // Move each of the elements:
for (SIZE_T i=0; i<nMaxCopy; i++) { ::FusionWin32CopyContents(prgtTempNewArray[i], rprgtArrayInOut[i]); } }
// We're done. Blow away the old array and put the new one in its place.
::FusionFreeArray(nOldSize, rprgtArrayInOut); rprgtArrayInOut = prgtTempNewArray; prgtTempNewArray = NULL; }
fSuccess = TRUE;
Exit: if (prgtTempNewArray != NULL) ::FusionFreeArray(nNewSize, prgtTempNewArray);
return fSuccess; }
#define MAKE_CFUSIONARRAY_READY(Typename, CopyFunc) \
template<> inline BOOL FusionWin32CopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \ FN_PROLOG_WIN32 IFW32FALSE_EXIT(rtDest.CopyFunc(rcSource)); FN_EPILOG } \ template<> inline HRESULT FusionCopyContents<Typename>(Typename &rtDest, const Typename &rcSource) { \ HRESULT hr = E_FAIL; FN_TRACE_HR(hr); IFW32FALSE_EXIT(::FusionWin32CopyContents<Typename>(rtDest, rcSource)); FN_EPILOG } \ template<> inline VOID FusionMoveContents<Typename>(Typename &rtDest, Typename &rcSource) { \ FN_TRACE(); HARD_ASSERT2_ACTION(FusionMoveContents<Typename>, "FusionMoveContents not allowed in 99.44% of cases."); }
#endif // !defined(FUSION_ARRAYHELP_H_INCLUDED_)
|