STL list code fires assertion in VC++ 2005, but not in VC++ 2003.

J

Jason Doucette

I'm getting an assertion fire from a list iterator being checked
against NULL. This did not occur in VC++ 2003 (v7.1). Are there
changes that have been made to the STL between these versions that I
should know about? Any resources I should check into?

thanks,
Jason
 
V

Victor Bazarov

Jason said:
I'm getting an assertion fire from a list iterator being checked
against NULL.

Why are you doing that?
This did not occur in VC++ 2003 (v7.1). Are there
changes that have been made to the STL between these versions that I
should know about?

Probably. Actually, very likely.
Any resources I should check into?

FAQ 5.8

V
 
J

Jason Doucette

I'm getting an assertion fire from a list iterator being checked
Why are you doing that?

Because I have a function that reports whether or not an element is in
the list. If it is, it returns an iterator to that element. If it's
not, then it returns NULL.

Is this bad? It worked fine in VC++ 2003. But, the internals of the
STL list class doesn't seem to like it, anymore.
Probably. Actually, very likely.

I recall reading something a long time ago that they had made some
changes. I can't seem to find a decent resource on this, though.

Of what manual? The C++ Standard? I don't think it's publicly
available.

Thanks, Victor.

Jason
 
J

Jason Doucette

FAQ 5.8

Sorry, you mean the newsgroup FAQ, obviously, duh. I had forgotten
which newsgroup I was replying to.

cheers,
Jason
 
M

Marcus Kwok

Jason Doucette said:
I'm getting an assertion fire from a list iterator being checked
against NULL. This did not occur in VC++ 2003 (v7.1). Are there
changes that have been made to the STL between these versions that I
should know about? Any resources I should check into?

MS added "checked iterators" in VS 2005. See:
http://msdn2.microsoft.com/en-us/library/aa985965(VS.80).aspx

If you need more info on them, you should probably consult a Visual
Studio group.
 
J

Jason Doucette

Sorry, you mean the newsgroup FAQ, obviously, duh. I had forgotten
which newsgroup I was replying to.

....which is parashift.com's C++ FAQ Lite, which is not a FAQ just for
this group.

5.8 is about posting non-working code questions:
http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.8
Is this what you mean?

If so, I am not asking anyone to fix my code. I'm looking for a
resource (that I can research myself) about what changes were made to
the STL implementation from VC++ 2003 to VC++ 2005, so I can determine
what I'm now doing wrong.

Thanks,
Jason
 
M

Marcus Kwok

Jason Doucette said:
Because I have a function that reports whether or not an element is in
the list. If it is, it returns an iterator to that element. If it's
not, then it returns NULL.

Oh, I misinterpreted your original post. For containers, usually
functions that work with them are passed a start iterator
(container.begin()) and a one-past-the-last iterator (container.end()).
If the element isn't found, then the one-past-the-last iterator is
returned.
Is this bad? It worked fine in VC++ 2003. But, the internals of the
STL list class doesn't seem to like it, anymore.

Pointers are valid iterators, so my guess is that in VC++ 2003, the
iterators were implemented as pointers, so the comparison with NULL
worked. But then, in 2005, they added the checked iterators, which is
probably a whole class instead of a simple pointer, so the comparison
with NULL is no longer valid.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

I'm getting an assertion fire from a list iterator being checked
against NULL. This did not occur in VC++ 2003 (v7.1). Are there
changes that have been made to the STL between these versions that I
should know about? Any resources I should check into?

No, the STL is probably the same. But the implementation in VS might
have changed. Consider yourself lucky this probably means you have
uncovered a hidden bug in your code. By the way, what do you mean with
the iterator being checked against NULL? Who did the check?
 
J

Jason Doucette

MS added "checked iterators" in VS 2005. See:http://msdn2.microsoft.com/en-us/library/aa985965(VS.80).aspx

Marcus, this link lead me to the page I was looking for:

Breaking Changes in the Standard C++ Library
http://msdn2.microsoft.com/en-us/library/aa985946(VS.80).aspx

Specifically, this section:

"Debug Iterators
Applications built with a debug version of the C-Runtime Library and
which use iterators incorrectly might begin to see asserts at runtime.
To disable these asserts, you must define _HAS_ITERATOR_DEBUGGING to
0. For more information, see Debug Iterator Support."

So, I have been using iterators incorrectly this entire time. Perhaps
it has to do with me setting them to NULL, or not initializing them
explicitly to a value that makes sense. I am not sure, I'll have to
research this.

Thanks for your help,
Jason
 
A

Alan Johnson

Jason said:
...which is parashift.com's C++ FAQ Lite, which is not a FAQ just for
this group.

5.8 is about posting non-working code questions:
http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.8
Is this what you mean?

If so, I am not asking anyone to fix my code. I'm looking for a
resource (that I can research myself) about what changes were made to
the STL implementation from VC++ 2003 to VC++ 2005, so I can determine
what I'm now doing wrong.

Thanks,
Jason

Victor was almost certainly trying to hint that you should post some
code. Without seeing code that demonstrates the error, the best we can
do is guess.

From the rest of this thread, it appears to me that you are under the
impression that iterators are pointers, which is not (necessarily) true.
Setting an iterator to NULL, for example, doesn't make any sense. You
should treat an iterator as an opaque object that can only be used in
the expressions guaranteed by the standard. If you don't have a copy of
the standard, the following is reasonably close to correct:
http://www.sgi.com/tech/stl/table_of_contents.html

The equivalent of a NULL pointer for iterators is the past-the-end
iterator (what you get from container.end()). The correct way to do
what you described elsewhere in the thread would look something like this:

template <typename T>
typename std::list<T>::iterator some_algorithm(
const std::list<T> & lst, const T & value)
{
// Do whatever and return an iterator.

// Not found, return the past the end iterator.
return lst.end();
}

void some_function()
{
std::list<int> lst;
// ...

std::list<int>::iterator i = some_algorithm(lst, 42);
if (i == lst.end())
{
// Handle not found case.
}
}
 
M

Marcus Kwok

Jason Doucette said:
So, I have been using iterators incorrectly this entire time. Perhaps
it has to do with me setting them to NULL, or not initializing them
explicitly to a value that makes sense.

Yes, I think this is the case. See Alan Johnson's post or my other post
for suggestions on how you should handle the not-found case instead of
trying to return NULL.
 
J

Jason Doucette

No, the STL is probably the same. But the implementation in VS might
have changed. Consider yourself lucky this probably means you have
uncovered a hidden bug in your code. By the way, what do you mean with
the iterator being checked against NULL? Who did the check?

I have a function that checks a list for some data, by iterating
through it. If it finds the element, it returns the iterator to it.
If not, it returns NULL. The caller of this function checks the
return value (an iterator) to see if it's NULL or not. (Kind of like
a pointer, it either points to something, or NULL...)

std::list<myStruct> myList;
std::list<myStruct>::iterator myIterator;

for (myIterator = myList.begin(); myIterator != myList.end();
myIterator++)
{
if (myIterator != NULL) <----- BANG!
}

thanks,
Jason
 
J

Jason Doucette

Oh, I misinterpreted your original post. For containers, usually
functions that work with them are passed a start iterator
(container.begin()) and a one-past-the-last iterator (container.end()).
If the element isn't found, then the one-past-the-last iterator is
returned.

Oh... so I should probably use this concept, as well. To be
consistent. In fact, it may be the only correct way to do it.

I should note that the assertion fires when the iterator is NOT NULL,
but is a legit element. It appears that it cannot be compared to
NULL. It used to be ok to treat it like any old pointer (perhaps
because VC++ 7.1 didn't care), but now it seems this is not ok (which
was likely always the case, for STL).
Pointers are valid iterators, so my guess is that in VC++ 2003, the
iterators were implemented as pointers, so the comparison with NULL
worked. But then, in 2005, they added the checked iterators, which is
probably a whole class instead of a simple pointer, so the comparison
with NULL is no longer valid.

Right. The checked iterators do check that they are in range. But,
my issue isn't an out-of-range one (although, eventually, it will be
when the function that does a search fails, and wants to return
NULL). My current issue is that an iterator that points to a real
element cannot be compared to NULL. Your diagnosis sounds correct.

Thanks, Marcus
Jason
 
J

Jason Doucette

Victor was almost certainly trying to hint that you should post some
code. Without seeing code that demonstrates the error, the best we can
do is guess.

Right. I didn't want people to solve my code problem. I just wanted
a resource section so I could solve it myself. But, you guys have
done an amazing job, nonetheless.
From the rest of this thread, it appears to me that you are under the
impression that iterators are pointers, which is not (necessarily) true.
Setting an iterator to NULL, for example, doesn't make any sense. You
should treat an iterator as an opaque object that can only be used in
the expressions guaranteed by the standard.

I think this is exactly my problem.
If you don't have a copy of
the standard, the following is reasonably close to correct:
http://www.sgi.com/tech/stl/table_of_contents.html

Thank you.
The equivalent of a NULL pointer for iterators is the past-the-end
iterator (what you get from container.end()). The correct way to do
what you described elsewhere in the thread would look something like this:

template <typename T>
typename std::list<T>::iterator some_algorithm(
const std::list<T> & lst, const T & value)
{
// Do whatever and return an iterator.

// Not found, return the past the end iterator.
return lst.end();
}

void some_function()
{
std::list<int> lst;
// ...

std::list<int>::iterator i = some_algorithm(lst, 42);
if (i == lst.end())
{
// Handle not found case.
}
}

Right, thanks a lot, Alan! This is exactly what I will need to
change.

Jason
 
V

Victor Bazarov

Jason said:
I have a function that checks a list for some data, by iterating
through it. If it finds the element, it returns the iterator to it.
If not, it returns NULL. The caller of this function checks the
return value (an iterator) to see if it's NULL or not. (Kind of like
a pointer, it either points to something, or NULL...)

std::list<myStruct> myList;
std::list<myStruct>::iterator myIterator;

for (myIterator = myList.begin(); myIterator != myList.end();
myIterator++)
{
if (myIterator != NULL) <----- BANG!
}

This code isn't even remotely close to the real code, Jason. There
is no "function", there is no "caller"...

Does the caller have access to the list? If he does, returning the
'end()' is much better than relying on the iterator's comparability
with NULL. It he does not, why does it have to be an iterator? Make
your function return a pointer, to the contained element if success
and NULL if not.

V
 
J

Jason Doucette

This code isn't even remotely close to the real code, Jason. There
is no "function", there is no "caller"...

Sorry, I determined the function / caller wasn't the issue. It was
the iterator being compared to NULL, when it was set to being
something legitimate (between myList.begin() and myList.end()). So,
instead of making the problem more complex, I shortened it down as
much as I could, to remove all things that were irrelevant.

I believe the issue is resolved... I've been using iterators like
pointers, which is no longer safe since the switch from VC++7.1 to VC+
+8.0, as mentioned a few times elsewhere. So, I'll rework my code as
you and others have suggested, and I am confident it will work. I'll
post back and let you know.
Does the caller have access to the list? If he does, returning the
'end()' is much better than relying on the iterator's comparability
with NULL. It he does not, why does it have to be an iterator? Make
your function return a pointer, to the contained element if success
and NULL if not.

Yes, the caller does have access to the list, so returning end() is an
option, and sounds like something much better to use. I didn't know
it was standard practice to use this for something "out of range". (I
guess pointers / NULL just seemed intuitive.)

The caller makes use of the information within the element it gets (if
not NULL), and then deletes it from the list. So, having the iterator
to this element is ideal, since deleting it via erase() is very easy,
as it takes in an iterator to the element you want erased.

Thanks for your time,
Jason
 
G

Gavin Deane

Yes, the caller does have access to the list, so returning end() is an
option, and sounds like something much better to use. I didn't know
it was standard practice to use this for something "out of range". (I
guess pointers / NULL just seemed intuitive.)

The caller makes use of the information within the element it gets (if
not NULL), and then deletes it from the list. So, having the iterator
to this element is ideal, since deleting it via erase() is very easy,
as it takes in an iterator to the element you want erased.

It is ideal - and that's not by happy coincidence, it's by design. Now
you've discovered the idomatic way to use iterators, have a look at
how the standard library algoriths fit together with containers and
iterators. For example, it sounds like there could be a way to replace
your function with a single call to std::find. Might or might not be
easy - and if it is, it might or might not be worth it if you've got
your function already written and tested, but it will be useful
knowledge for next time.

Gavin Deane
 
R

red floyd

Jason Doucette wrote:
My current issue is that an iterator that points to a real
element cannot be compared to NULL.

Technically, that's always been the case.
 
J

James Kanze

[...]
Yes, the caller does have access to the list, so returning end() is an
option, and sounds like something much better to use. I didn't know
it was standard practice to use this for something "out of range". (I
guess pointers / NULL just seemed intuitive.)

It's the usual solution when the user is aware of the list.
Another, more general solution is to use Barton and Nackman's
Fallible as a return value.
The caller makes use of the information within the element it gets (if
not NULL), and then deletes it from the list. So, having the iterator
to this element is ideal, since deleting it via erase() is very easy,
as it takes in an iterator to the element you want erased.

This sounds like something Fallible was designed for. The
client has no need to "know" anything about the list.
 
J

Jason Doucette

It is ideal - and that's not by happy coincidence, it's by design.

Yes.
Now
you've discovered the idomatic way to use iterators, have a look at
how the standard library algoriths fit together with containers and
iterators. For example, it sounds like there could be a way to replace
your function with a single call to std::find. Might or might not be
easy - and if it is, it might or might not be worth it if you've got
your function already written and tested, but it will be useful
knowledge for next time.

I didn't use std::find, since I had to search for a match of a struct
field, in a list of such structs. But, I could have defined the
equality operator == for this struct, and used std::find. The actual
search code is so small (just a simple for loop), that it's no big
deal, really. But, I'll keep this in mind for the next time.

Jason
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top