For Programmers: Free Programming Magazines  


Home > Archive > VC STL > March 2006 > Overly aggressive checking in new STL (Visual Studio 2005)?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Overly aggressive checking in new STL (Visual Studio 2005)?
Records For Living Support

2006-03-13, 7:02 pm

Suppose I have a vector<char> v;

And suppose I have a function that wants to work with char* (say its
historical, or just doesn't know if the chars* come from a vector or
basic_string, or whatever.

void f (const char* start, const char* end)
{
// do some nice stuff on chars between start/end
}

Before this release, I could do:
f (&*v.begin (), &*v.end ())

and this would do the right thing, and as nearly as I can tell, is perfectly
legitimate STL/C++ (though I do wish there was a neater way to convert
iterators to pointers).

Sadly - this now BREAKS in Visual Studio 2005 - due to the
_HAS_ITERATOR_DEBUGGING enhancements.

I really I can turn off ALL debug checking, but I really do like the idea of
having some - perhaps most of the checking that you've implemented. I just
don't want perfectly legitimate reasonable code to be treated as broken!

What is the best (compatable with your library, and the STL standard) way to
get convert an iterator to a pointer (for a vector - or basic_string). I know
its not always legit for ANY container type (which is why you have the
concept of iterators to begin with ) - but it IS always legit for
vector/basic_string - at least from my reading of their definitions.

Any hints appreciated.

Lewis.
Jeff F

2006-03-13, 7:02 pm

Records For Living Support wrote:
> Suppose I have a vector<char> v;
>
> And suppose I have a function that wants to work with char* (say its
> historical, or just doesn't know if the chars* come from a vector or
> basic_string, or whatever.
>
> void f (const char* start, const char* end)
> {
> // do some nice stuff on chars between start/end
> }
>
> Before this release, I could do:
> f (&*v.begin (), &*v.end ())


Which was always dangerous, and at a minimum undefined behaviour, if not
outright illegal. One of the basic tenets of iterators is that
'container.end' should _never_ be dereferenced.

Replace the above with f( &v[0], &v[0]+v.size() ). Assuming of course that
'f' is expecting a half open range where end is one past the end of the
sequence. And of course, you'll need to first check the precondition
!v.empty().

Jeff


Igor Tandetnik

2006-03-13, 7:02 pm

Records For Living Support
<RecordsForLivingSupport@discussions.microsoft.com> wrote:
> Suppose I have a vector<char> v;
>
> And suppose I have a function that wants to work with char* (say its
> historical, or just doesn't know if the chars* come from a vector or
> basic_string, or whatever.
>
> void f (const char* start, const char* end)
> {
> // do some nice stuff on chars between start/end
> }
>
> Before this release, I could do:
> f (&*v.begin (), &*v.end ())
>
> and this would do the right thing, and as nearly as I can tell, is
> perfectly legitimate STL/C++ (though I do wish there was a neater way
> to convert iterators to pointers).


I don't think it is legal C++. Past-the-end iterator is not required to
be dereferenceable. See C++ standard 24.1/5.

This should probably work:

f (&*v.begin (), &*v.begin () + v.size());

> What is the best (compatable with your library, and the STL standard)
> way to get convert an iterator to a pointer (for a vector - or
> basic_string). I know its not always legit for ANY container type
> (which is why you have the concept of iterators to begin with ) - but
> it IS always legit for vector/basic_string - at least from my reading
> of their definitions.


A vector is required to store its values in a contiguous block of memory
by resolution to DR#69 - it was not required in the original standard.

http://www.open-std.org/jtc1/sc22/w...defects.html#69

The situation with basic_string is less clear-cut - see DR#530:

http://www.open-std.org/jtc1/sc22/w...active.html#530

--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


David Wilkinson

2006-03-13, 7:02 pm

Records For Living Support wrote:

> Suppose I have a vector<char> v;
>
> And suppose I have a function that wants to work with char* (say its
> historical, or just doesn't know if the chars* come from a vector or
> basic_string, or whatever.
>
> void f (const char* start, const char* end)
> {
> // do some nice stuff on chars between start/end
> }
>
> Before this release, I could do:
> f (&*v.begin (), &*v.end ())
>
> and this would do the right thing, and as nearly as I can tell, is perfectly
> legitimate STL/C++ (though I do wish there was a neater way to convert
> iterators to pointers).
>
> Sadly - this now BREAKS in Visual Studio 2005 - due to the
> _HAS_ITERATOR_DEBUGGING enhancements.
>
> I really I can turn off ALL debug checking, but I really do like the idea of
> having some - perhaps most of the checking that you've implemented. I just
> don't want perfectly legitimate reasonable code to be treated as broken!
>
> What is the best (compatable with your library, and the STL standard) way to
> get convert an iterator to a pointer (for a vector - or basic_string). I know
> its not always legit for ANY container type (which is why you have the
> concept of iterators to begin with ) - but it IS always legit for
> vector/basic_string - at least from my reading of their definitions.
>
> Any hints appreciated.
>
> Lewis.


Lewis:

What about

f(&v[0], &v[0] + v.size());

Does this work in VC8? It does in VC7.1. But so does your version.

BTW, I didn't think it was ever guaranteed that std::string is
contiguous. But std::string has c_str() member, so it doesn't need to be.

David Wilkinson
Records For Living Support

2006-03-13, 7:02 pm

I agree that the *v.end () part is a bit sketchy - just the only way I could
ever find of accessing the real ptr to START/END of the array

Thanks for all of your help. Unfortunately - it really misses the point and
doesn't help.

Suppose the vector is EMPTY. Then f(&v[0],...) is just as much a problem as
&*v.end ().

In fact - it fails just as resoundingly on MSFTs new STL implementation.

I feel a bit sheepish about the &*v.end () thing. I justified it in my head
as not REALLY defererecning v.end () (in fact - no compiler I'm aware of ever
actually intepretted &*v.end() as dereferencing v.end()). BUt certainly I
could imagine one doing so.

So -then - there is no way to fix:

template <typename CONTAINER>
inline typename CONTAINER::value_type* I
TR2PTR (typename CONTAINER::iterator
i)
{

return &*i;
//return i.operator-> ();
}





"Jeff F" wrote:

> Records For Living Support wrote:
>
> Which was always dangerous, and at a minimum undefined behaviour, if not
> outright illegal. One of the basic tenets of iterators is that
> 'container.end' should _never_ be dereferenced.
>
> Replace the above with f( &v[0], &v[0]+v.size() ). Assuming of course that
> 'f' is expecting a half open range where end is one past the end of the
> sequence. And of course, you'll need to first check the precondition
> !v.empty().
>
> Jeff
>
>
>

Stephen Howe

2006-03-13, 7:02 pm

> Before this release, I could do:
> f (&*v.begin (), &*v.end ())


Which is illegitimate. You can't dereference v.end() and take the address.
Same is true of all the other STL containers. You are better off passing the
size as 2nd argument and doing any address calculation inisde f.

> I really I can turn off ALL debug checking, but I really do like the idea

of
> having some - perhaps most of the checking that you've implemented. I just
> don't want perfectly legitimate reasonable code to be treated as broken!


Not legitimate. The code _IS_ broken (and always has been).
Previous compilers let you get aways with it. This one does not.

Cheers

Stephen Howe


Records For Living Support

2006-03-13, 7:02 pm

I'll gladly accept that my code is/was broken, and move on. Though the code
may have been broken, the idea behind it was not.

I like the C++ iterator paradigm - passing start/end iterators around and
using that consistently isntead of start/count.

Not all code uses templates - however - and sometimes - for example with IO
or other shared routines - its helpful to pass a bunch of data.

For example -

f (const T* start, const T* end);

The caller of f() might have been using a vector, memory-mapped file, or
some other source of data for the array of T's. It shouldn't matter.

I want to be able to easily convert to C++-style iterator (start/end)
pointers - and use them inside of f().

Now - you may argue:
(a) this is crazy - use templates
The problem with this argument is that - though templates are
quite powerful - they are not always the easiest thing to use, and one
shouldn't ahve to use templates ALWAYS - FOR EVERYTHING. Simply requring that
the data passed to 'f' is a contiguous block of 'T's - in this application -
makes sense.

(b) Jump thorugh hoops to call f - if you are calling it with a vector,
as in:
if (v.empty ()) {
f (NULL, 0);
}
else {
f (&v[0], &v[v.size()]);
}
Well, there is some merit to this argument, but C++ is meant to be
a tool to make programming easier. I just find :
f (&v[0], &v[v.size ()]);
or
f(&*v.begin(),&*v.end ());
much clearer and simpler (when done alot of times) - then the
if/else test above.

I hope one of you can either point out an easy way for me to
convert start/end iterators to start/end pointers, so that I can easily use
the STL/iterator paradigm in code that isn't template-burdoned; or perhaps
you can at least make me feel better about the fact that this is no longer
possible.










"Stephen Howe" wrote:

>
> Which is illegitimate. You can't dereference v.end() and take the address.
> Same is true of all the other STL containers. You are better off passing the
> size as 2nd argument and doing any address calculation inisde f.
>
> of
>
> Not legitimate. The code _IS_ broken (and always has been).
> Previous compilers let you get aways with it. This one does not.
>
> Cheers
>
> Stephen Howe
>
>
>

Stephen Howe

2006-03-13, 7:02 pm


"Records For Living Support"
<RecordsForLivingSupport@discussions.microsoft.com> wrote in message
news:D982AD8F-F5C6-452B-B8B4-16B9F3105AED@microsoft.com...
> I'll gladly accept that my code is/was broken, and move on. Though the

code
> may have been broken, the idea behind it was not.


How about

f ((v.empty() ? NULL : &v[0]), (v.empty() ? NULL : &v[0] + v.size()));

You get 2 NULL arguments if empty otherwise begin/end addresses.

Stephen Howe


Tom Widmer [VC++ MVP]

2006-03-14, 7:58 am

Records For Living Support wrote:
> I'll gladly accept that my code is/was broken, and move on. Though the code
> may have been broken, the idea behind it was not.
>
> I like the C++ iterator paradigm - passing start/end iterators around and
> using that consistently isntead of start/count.
>
> Not all code uses templates - however - and sometimes - for example with IO
> or other shared routines - its helpful to pass a bunch of data.
>
> For example -
>
> f (const T* start, const T* end);
>
> The caller of f() might have been using a vector, memory-mapped file, or
> some other source of data for the array of T's. It shouldn't matter.
>
> I want to be able to easily convert to C++-style iterator (start/end)
> pointers - and use them inside of f().


Well, any solution should know to either prevent the use of std::list or
to make a contiguous copy of it. See below.

>
> Now - you may argue:
> (a) this is crazy - use templates
> The problem with this argument is that - though templates are
> quite powerful - they are not always the easiest thing to use, and one
> shouldn't ahve to use templates ALWAYS - FOR EVERYTHING. Simply requring that
> the data passed to 'f' is a contiguous block of 'T's - in this application -
> makes sense.


Yes, and sometimes you can't use templates at all (e.g. for virtual
functions).


> I hope one of you can either point out an easy way for me to
> convert start/end iterators to start/end pointers, so that I can easily use
> the STL/iterator paradigm in code that isn't template-burdoned; or perhaps
> you can at least make me feel better about the fact that this is no longer
> possible.


If in doubt, write helper functions:

template <class T, class Alloc>
T* begin_ptr(std::vector<T, Alloc>& v)
{
if (v.empty())
return 0;
else
return &v[0];
}

template <class T, class Alloc>
T* end_ptr(std::vector<T, Alloc>& v)
{
if (v.empty())
return 0;
else
return &v[0] + v.size();
}

//overload for const and for std::basic_string if you're happy to make
//the nonstd contiguity assumption (or make a const only version using
//c_str()).


//fails to compile for e.g. std::list, which is good!
f(begin_ptr(v), end_ptr(v));

Tom
Hendrik Schober

2006-03-31, 4:09 am

Stephen Howe <stephenPOINThoweATtns-globalPOINTcom> wrote:
> "Records For Living Support"
> <RecordsForLivingSupport@discussions.microsoft.com> wrote in message
> news:D982AD8F-F5C6-452B-B8B4-16B9F3105AED@microsoft.com...
>
> How about
>
> f ((v.empty() ? NULL : &v[0]), (v.empty() ? NULL : &v[0] + v.size()));


<shudders>

> You get 2 NULL arguments if empty otherwise begin/end addresses.
>
> Stephen Howe


Schobi

--
SpamTrap@gmx.de is never read
I'm Schobi at suespammers dot org

"The sarcasm is mightier than the sword."
Eric Jarvis


Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com