Jump to content
  • Advertisement
Sign in to follow this  
kRogue

stl::vector, .at(), operator[ ]

This topic is 4953 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everyone, I was wondering if there was an (easy) way to do this: using stl::vector's for my array stuff, currently I am using myVector to get the i'th element of a vector.. now the operator[] does NOT do bounds checking, where as myVeector.at(i) does do bounds checking... is there an easy way via a compiler option (using g++) so that I can force a bounds check on [], rather that change all my [] calls it .at() calls, debug my code and then change back to using []? Best Regards

Share this post


Link to post
Share on other sites
Advertisement
There's a couple of ways you could hack around it but stop a moment and think about the reason for doing so if you decide to do so. If you go trample outside your array bounds when in debug mode how can you be so sure that under certain circumstances you won't be doing it when you go into release mode?

If you can't be 100% sure that you're not going to go out of bounds (like using i != v.size() for loop termination etc) then it's probably, in the long run, a lot safer to just stick with the at calls instead.

Anyhow, if you still want to skip the saftey-belt when going release simple condiditional compilation and a typedef should solve it easily, implementation left as an exercise to the reader but should take ~3minutes.

Share this post


Link to post
Share on other sites
Don't know about gcc + libstdc++, but the Dinkumware STL used by Microsoft's Visual Studio actually does bounds checking in operator [] (via an assertion; that's legal, it just mustn't throw an exception like at() is guaranteed to do, regardless of debug or release mode). Maybe that's sufficient for you.

Otherwise, well, I think you would be better off by replacing any [] with .at() instead of hacking something together (like a wrapper which calls .at() in operator[] :))

-Markus-

Share this post


Link to post
Share on other sites
truth is that all I want is an assert() so that I can debug easily... I guess I will just make my hack real quick.... as for my motivation: I just want to _CHECK_ that I do not go outside my array bounds in an automated way... as of right now my code crashes occasionally, and has occasional odd behavious... thus I am convinced that it is a bad memory writing (before I was using raw pointers, and took the day to move it to stl::vector) but still have wierd seg faults which suggest going out of bounds on my arrays.... I stay away from exception handling like the plague.... some love it, but I am not so crazy about typing "catch" everytime I do an array access... I jsut want to know where (and if) I go out of my array bounds...


Best Regards

Share this post


Link to post
Share on other sites
maybe do


template<typename T>
class TestVector : std::vector<T> {
public:
//constructors etc.
//...
reference operator[](size_type index) {
return at(index);
}
};



or something.

Share this post


Link to post
Share on other sites
I did almost the exact samething as posed by petewood for the hack to force vector bounds checking, though I was a bit more paranoid:


template<class T>
class kector:public vector<T>
{
public:
kector(int sz):vector<T>(sz) {}
kector(void):vector<T>() {}
kector(const kector &obj):vector<T>(obj) {}

reference
operator[](size_t __n)
{
ASSERT(0<=__n && __n<size());
return *(begin() + __n);
}

const_reference
operator[](size_t __n) const
{
ASSERT(0<=__n && __n<size());
return *(begin() + __n);
}

};




though, after I changed by vectors to kectors, I never got an out of bounds error, ever.... just to check that it was actually checking the bounds I went ahead and tested it and deliberatly did an array acces out of bounds, and the ASSERT, well asserted.... so now I know that my array writing is within bounds always.... damn... maybe I ahve a wild poiner somewhere... but I ahve been writing my stuff uber-paranoid like though.....


one thing that bothered me was the the compiler (g++) gave a warning when I used the return types const_reference and reference for the operator calls.. a quick look into the file of the #include <vector> and within it, to something like "bits/stl_vector" came to the lines within the declaration of the vector template:


typedef _Tp value_type;
typedef value_type& reference;
typedef const value_type& const_reference;



but when I made my operators return types "const T&" and "T&" the compiling failed when it came up to a vector<bool>.. any thoughs?

Share this post


Link to post
Share on other sites
Use 'typedef std::vector<T>::whatever whatever;'.

Speaking of code to help find bugs, here's something I wrote once:


#ifndef DETECTOR_HPP
#define DETECTOR_HPP

/** \file detector.hpp
\brief The declaration of the DamageDetector class.
*/


#include "assert.hpp"

/** \brief A DamageDetector object is used for detecting damaged objects.

If it somehow gets overwritten, then copying, destroying, assigning,
and invoking check will (or at least probably; there's a small chance
it might get over written to look like it's in a valid state) result in
an assertion failure.

Destroying an object will put a DamageDetector object into an invalid state,
so attempting to use a destroyed object (i.e., with a stale reference, or a
dangling pointer) will also cause an assertion failure.

Can also help find buffer overflows and fence post errors, as such an object
will not have been constructed, and will generate an error if you try to
use them.

If debugging is disabled, DamageDetector does nothing.

So, add a public DamageDetector in your classes and remember to check it at
the start of your functions to help ensure you're using a real object, and
not just garbage floating around somewhere.
*/


class DamageDetector
{
#ifndef NDEBUG // Debug version:
protected:
DamageDetector *self;

public:

/** \brief DamageDetector default constructor.
*/

DamageDetector(): self(this)
{}

/** \brief DamageDetector copy constructor.
\param ref The DamageDetector being copied.

Checks the object it's being copied from for damage, other than
that it's identical to default constructor.
*/

DamageDetector(const DamageDetector &ref): self(this)
{
ref.check();
}

/** \brief DamageDetector destructor.

Checks itself for damage and then puts itself into an invalid state
so that it will notice if you try to use it after it's been destroyed.
*/

~DamageDetector()
{
check(); // This can throw an exception, and exceptions shouldn't be thrown from destructors. This should be interesting.
self = 0;
}

/** \brief DamageDetector copy constructor.
\param ref The DamageDetector being assigned from.
\return A reference to itself.

Checks itself and the reference for damage, then returns a reference to itself.
*/

DamageDetector & operator = (const DamageDetector &ref)
{
check();
ref.check();
return *this;
}

/** \brief Equality operator
\param ref The DamageDetector being compared.
\return true.

Checks itself and the reference for damage, then returns true.
*/

bool operator == (const DamageDetector &ref) const
{
check();
ref.check();
return true;
}


/** \brief Inequality operator
\param ref The DamageDetector being compared.
\return false.

Checks itself and the reference for damage, then returns false.
*/

bool operator != (const DamageDetector &ref) const
{
check();
ref.check();
return true;
}

/** \brief Checks for damage.

If there is a problem, an assertion failure happens. You should try to call this at the start of all your functions.

\see assert setAssertAction
*/

void check(void) const
{
assert(this && this == self);
self = this; // If this did screw up, reset it to a proper value so this doesn't continually show up.
}

#else // Release version; does nothing.
public:
void check(void) const
{}
#endif
};

#endif




Stick one of this in your classes and call check() on it at the start of all your functions.

Helps catch buffer overruns, data corruption, dangling pointers and references, using uninitialized memory, bad pointers, etc.

Share this post


Link to post
Share on other sites
You could allways open up your headers and throw an assert() in there yourself... not really recommended from a system's administration POV... but if you're lazy like me, it's the easiest way :P.


e.g. for vector<> on my debian box I'd go into:

/usr/include/c++/3.3/bits/stl_vector.h (not .hh or .hpp like it should be :-()

And add #include <cassert> with the rest of the headers in the include guard... and change:

reference
operator[](size_type __n) { return *(begin() + __n); }

and:

const_reference
operator[](size_type __n) const { return *(begin() + __n); }

to:

reference
operator[](size_type __n) { assert( __n < this->size() ); return *(begin() + __n); }

and:

const_reference
operator[](size_type __n) const { assert( __n < this->size() ); return *(begin() + __n); }

Patches should be able to work even :).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!