Your solution worked. I understand that the code was broken previously and
I'm just glad I know where we are going wrong now thanks to you. I'm no
expert on STL and I guess I better start reading up on it.
Once again, many many thanks.
Colin J Paterson wrote:
iNumElements = m_Tables.GetSize();
pEnumerator->Init(m_Tables.GetAddress(0),
m_Tables.GetAddress(iNumElements), NULL, AtlFlagNoCopy);
[...]
If I make the changes you say I get a Automation Error in Visual Basic
when it returns the empty variant. The get address function seems to
deliberately return past the the end of the vector as described in the
MSDN for CComEnumImpl::Init.
Okay, then you know for the future what to document there. Anyhow,
comparing
this:
if(IsEmpty() || (iIndex < 0) || iIndex > m_vector.size())
{
return &m_emptyVariant;
}
and this:
if( iIndex < m_vector.size())
return &m_vector[iIndex].m_Variant;
return &m_emptyVariant;
The above variant is definitively broken C++ code. Nothing about external
requirements will ever change that. I repeat, you must not call vector's
operator[] with a value equal to or greater than its size. Period, no
discussion here.
Now, for the enumerator's Init(), you still need two pointers that
represent
the start and past-the-end of the sequence. If the sequence is empty, you
could return NULL or some other dummy value for index=0 and index=size.
Otherwise, you could return the address of the first element
(vector::front()) plus the index. I would consider creating a get_range()
function instead, because the habit of letting a one-off index slip
through
is really bad and confusing. At the very least, it deserves being
documented.
I stress, this worked fine in VC6
Let me repeat what I wrote:
This seems to work in Visual C++ 6 but in 2005 it throws a subscript
out of range error? Any ideas why?
Simply because its standardlibrary is better, in debug mode it features
a
checked implementation like STLport that catches several errors where
the
standard just shrugs and says "undefined behaviour, no diagnostic
required". The code was broken all along though.
Just for completeness, there are two additional alternatives you could
use:
1. Move the computation of the past-the-end pointer to outside the array
class:
pEnumerator->Init(m_Tables.GetAddress(0),
m_Tables.GetAddress(0)+iNumElements,
NULL, AtlFlagNoCopy);
This doesn't cater for an empty container though, but I think that's an
error all along, which might deserve special handling anyway.
2. Always maintain an additional object in the array. You would then
insert
new elements before that last element instead of using push_back() etc,
but
you would have the guarantee that the contained array is never empty and
that the past-the-end element exists and is accessible. You would have to
adjust methods that use the size of the vector accordingly.
Uli