No boundschecking?

M

mike3

Hi.

I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

For example, consider the piece of code at the end of this message.
Should I use a boundscheck in there to check whether or not the ranges
specified are in-bounds or out-of-bounds? But if I do, then it makes
that seemingly nice piece of code real messy and I don't like it so
much.

Would it be a good idea therefore to perhaps build some sort of "safe
iterator" class that duplicates the functionality of iterator (in
fact, encapsulating an iterator inside it), but includes the
boundschecking for debug builds (otherwise, the safe iterator becomes
an ordinary iterator for maximum performance.)?

--- Code snippet:
/* Add two RawDigit sections.
* Parameters:
* a: First RawDigit.
* b: Second RawDigit.
* rstart: Starting digit of section of this to use.
* rend: Ending digit of section of this to use.
* astart: Starting digit of section of a to use.
* aend: Ending digit of section of a to use.
* bstart: Starting digit of section of b to use.
* bend: Ending digit of section of b to use.
*
* Returns: carry.
*
* Operation: *this = a + b.
*/
Digit RawDigit::Add(const RawDigit &a, const RawDigit &b,
std::size_t rstart, std::size_t rend,
std::size_t astart, std::size_t aend,
std::size_t bstart, std::size_t bend)
{
/* Get lengths */
std::size_t rLength(rend - rstart + 1);
std::size_t aLength(aend - astart + 1);
std::size_t bLength(bend - bstart + 1);

/* Set up iterators */
DigitIterator ri(digits.begin() + rstart);
c_DigitIterator ai(a.digits.begin() + astart);
c_DigitIterator bi(b.digits.begin() + bstart);

/* Make b the shortest operand */
if(aLength < bLength)
return(Add(b, a, rstart, rend, bstart, bend, astart, aend));

/* Now do the first part of the addition, involving both a and b
*/
Digit carry(0);
for(std::size_t i(0);i<bLength && i<rLength;++i,++ri,++ai,++bi)
*ri = DigitAdd(*ai, *bi, &carry);

/* Now do the second part of the addition, involving only a,
* if necessary.
*/
for(std::size_t i(bLength);i<aLength && i<rLength;++i,++ri,++ai)
*ri = DigitAdd(*ai, 0, &carry);

/* Now do the third and final part of the addition, which
* just pads the result with zeroes and propagates the carry,
* if necessary.
*/
for(std::size_t i(aLength);i<rLength;++i,++ri)
{
*ri = carry;
carry = 0;
}

/* Done! */
return(carry);
}
---
 
V

Victor Bazarov

mike3 said:
I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

You should probably find a decent implementation of the Standard
library. Or just tweak yours.
For example, consider the piece of code at the end of this message.
Should I use a boundscheck in there to check whether or not the ranges
specified are in-bounds or out-of-bounds? But if I do, then it makes
that seemingly nice piece of code real messy and I don't like it so
much.

The implementation (and essentially the design) comes from the set
of requirements. Do your requirements include checking bounds in
the release mode? Can bounds be ignored by the users of your class
(even if it's you)? If they can (and essentially allowed to) and
you want to make your program robust enough to withstand those
"attacks" on the boundaries, then you _have_to_ check them yourself.
If you don't want to check and instead need to rely on the client's
code to check the bounds, then you need to document that crossing
the boundaries has undefined behaviour (just like the Standard says)
and put the burden of dealing with UB on the client.

V
 
M

mike3

You should probably find a decent implementation of the Standard
library.  Or just tweak yours.


The implementation (and essentially the design) comes from the set
of requirements.  Do your requirements include checking bounds in
the release mode?  Can bounds be ignored by the users of your class
(even if it's you)?  If they can (and essentially allowed to) and
you want to make your program robust enough to withstand those
"attacks" on the boundaries, then you _have_to_ check them yourself.
If you don't want to check and instead need to rely on the client's
code to check the bounds, then you need to document that crossing
the boundaries has undefined behaviour (just like the Standard says)
and put the burden of dealing with UB on the client.

But is it a bad idea to put code blocks like

if((rstart < 0) || (rstart >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);
if((rend < 0) || (rend >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);
if((astart < 0) || (astart >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);
if((aend < 0) || (aend >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);
if((bstart < 0) || (bstart >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);
if((bend < 0) || (bend >= GetLength())) throw
FG3DException(FG3D_INDEX_OUT_OF_RANGE);

all over the place? To me it looks silly and poor. Is it? If so,
what's the right way to do the bounds-checking?

Considering that on more than one occasion I have accidentally overrun
bounds and created strange bugs in my programs that were a bear to
track down, having a check seems like a good idea.
 
J

Jerry Coffin

Hi.

I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

The first question would be why you're using iterators at all. Your code
seems to be based around indexes instead of iterators. In that case,
you're probably better off just using indexes throughout -- and vector
does provide (optional) bounds-checking on indexes (you use vect if
you don't want bounds-checking, and vect.at(i) if you do want bounds
checking).
 
M

mike3

I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

The first question would be why you're using iterators at all. Your code
seems to be based around indexes instead of iterators. In that case,
you're probably better off just using indexes throughout -- and vector
does provide (optional) bounds-checking on indexes (you use vect if
you don't want bounds-checking, and vect.at(i) if you do want bounds
checking).


But what about if I want to disable the bounds-checking for
performance
purposes, and enable it for debug purposes? That's what I want:
boundschecking
I can turn on and off.
 
J

Jerry Coffin

[ ... ]
But what about if I want to disable the bounds-checking for
performance purposes, and enable it for debug purposes? That's what
I want: boundschecking I can turn on and off.

It's not as pretty as anybody would like, but one easy way is to use
your own preprocessor-dependent definitions, something like:

class RawDigit {
std::vector<Digit> digits;

#ifdef DEBUG
Digit &operator[](std::size_t index) {
return digits.at(index);
}
Digit Operator[](std::size_t index) const {
return digits.at(index);
}
#else
Digit &operator[](std::size_t index) {
return digits[index];
}

Digit operator[](std::size_t index) const {
return digits[index];
}
#endif

The rest of your code uses these instead of using the vector directly.
 
M

mike3

[ ... ]
But what about if I want to disable the bounds-checking for
performance purposes, and enable it for debug purposes? That's what
I want: boundschecking I can turn on and off.

It's not as pretty as anybody would like, but one easy way is to use
your own preprocessor-dependent definitions, something like:

class RawDigit {
    std::vector<Digit> digits;

#ifdef DEBUG
    Digit &operator[](std::size_t index) {
        return digits.at(index);
    }
    Digit Operator[](std::size_t index) const {
        return digits.at(index);
    }
#else
    Digit &operator[](std::size_t index) {
        return digits[index];
    }

    Digit operator[](std::size_t index) const {
        return digits[index];
    }
#endif

The rest of your code uses these instead of using the vector directly.

Hmm. This might work, however won't the calling
overhead to the operator[] be an issue? (I take
it you can't just tell the compiler to inline these.)
I'll have to bench it and see how fast it goes.
 
I

Ian Collins

mike3 said:
[ ... ]
But what about if I want to disable the bounds-checking for
performance purposes, and enable it for debug purposes? That's what
I want: boundschecking I can turn on and off.
It's not as pretty as anybody would like, but one easy way is to use
your own preprocessor-dependent definitions, something like:

class RawDigit {
std::vector<Digit> digits;

#ifdef DEBUG
Digit &operator[](std::size_t index) {
return digits.at(index);
}
Digit Operator[](std::size_t index) const {
return digits.at(index);
}
#else
Digit &operator[](std::size_t index) {
return digits[index];
}

Digit operator[](std::size_t index) const {
return digits[index];
}
#endif

The rest of your code uses these instead of using the vector directly.

Hmm. This might work, however won't the calling
overhead to the operator[] be an issue? (I take
it you can't just tell the compiler to inline these.)
I'll have to bench it and see how fast it goes.

You don't have to tell the compiler, it will (or at least should!)
inline such trivial methods.
 
M

mike3

mike3 said:
[ ... ]
But what about if I want to disable the bounds-checking for
performance purposes, and enable it for debug purposes? That's what
I want: boundschecking I can turn on and off.
It's not as pretty as anybody would like, but one easy way is to use
your own preprocessor-dependent definitions, something like:
class RawDigit {
    std::vector<Digit> digits;
#ifdef DEBUG
    Digit &operator[](std::size_t index) {
        return digits.at(index);
    }
    Digit Operator[](std::size_t index) const {
        return digits.at(index);
    }
#else
    Digit &operator[](std::size_t index) {
        return digits[index];
    }
    Digit operator[](std::size_t index) const {
        return digits[index];
    }
#endif
The rest of your code uses these instead of using the vector directly.
Hmm. This might work, however won't the calling
overhead to the operator[] be an issue? (I take
it you can't just tell the compiler to inline these.)
I'll have to bench it and see how fast it goes.

You don't have to tell the compiler, it will (or at least should!)
inline such trivial methods.

So if my compiler doesn't inline that, then that
doesn't indicate poor code, it indicates a poor
compiler, which I should throw out and substitute
a better one for, right?

Obviously, looking at this there are no "perfect"
solutions (not a surprise, though.). This therefore
raises the question of whether any of the solutions
here are adequate for the purposes at hand.

The whole point of the routines I've been struggling
so hard to craft here was to try and minimize the
bug-susceptibility of my code, but I also need high
performance. Therefore, the question comes up of
what the best way is to code these core routines,
like the adder I posted. What is it?
 
E

Erik Wikström

Hi.

I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

Use an implementation of the library that implements bounds-checking. In
the latest version of VC++ they have quite a lot of it, they use
Dinkumware's libraries and I know some of (or all) of the checks are in
the version you can buy from Dinkumware.
 
M

mike3

Use an implementation of the library that implements bounds-checking. In
the latest version of VC++ they have quite a lot of it, they use
Dinkumware's libraries and I know some of (or all) of the checks are in
the version you can buy from Dinkumware.

But of course that's nonportable, meaning if one goes to a different
compiler with different libraries it may not happen. So is it really
a good idea to rely on it?

Furthermore, how good do you think the implementation of the bignum
addition I showed in my original post is?
 
P

Puppet_Sock

So if my compiler doesn't inline that, then that
doesn't indicate poor code, it indicates a poor
compiler, which I should throw out and substitute
a better one for, right?

Or possibly it is your expectations that are faulty.
Obviously, looking at this there are no "perfect"
solutions (not a surprise, though.). This therefore
raises the question of whether any of the solutions
here are adequate for the purposes at hand.

The whole point of the routines I've been struggling
so hard to craft here was to try and minimize the
bug-susceptibility of my code, but I also need high
performance. Therefore, the question comes up of
what the best way is to code these core routines,
like the adder I posted. What is it?

Questions that include such terms as "adequate"
and "best" mean you have a spec. I didn't see
one posted in this thread. So, it *looks* like
your spec is "I want my code good."

If you have a spec, code to it. If a particular
way of doing stuff does not meet that, find another
way of doing it.

If you don't have a spec, or if it does not include
specific performance levels you have to meet, then
you should relax. Do things "standard" rather than
worrying about getting them "best."

Also, you *need* a spec. How can you ever know if
some proposed solution is "adequate" if you have
not defined adequate?

There is no "best" way to do things. In this context,
"best" is the enemy of the good. Worrying about "best"
will simply cause you to develop poor coding habits.
"Oh, you shouldn't use that, it's not 'best.'"
Socks
 
M

mike3

Or possibly it is your expectations that are faulty.



Questions that include such terms as "adequate"
and "best" mean you have a spec. I didn't see
one posted in this thread. So, it *looks* like
your spec is "I want my code good."

If you have a spec, code to it. If a particular
way of doing stuff does not meet that, find another
way of doing it.

If you don't have a spec, or if it does not include
specific performance levels you have to meet, then
you should relax. Do things "standard" rather than
worrying about getting them "best."

Also, you *need* a spec. How can you ever know if
some proposed solution is "adequate" if you have
not defined adequate?

There is no "best" way to do things. In this context,
"best" is the enemy of the good. Worrying about "best"
will simply cause you to develop poor coding habits.
"Oh, you shouldn't use that, it's not 'best.'"
Socks

So should I just run with what I posted in my first
post on this thread, and just document that it does not
boundscheck? Or is that a bad idea?

The goal here is to alleviate the design problems that
were found with the first attempt of the program, and
were pointed out here:

http://groups.google.com/group/comp.lang.c++/msg/a66278b77597d648?dmode=source

QUOTE:
"The main design problem I saw, with just a cursory look at the code,
was
a mix of very high level (abstract operations) and low level
(pointers,
casting), an abstraction gap, indicating one or more missing
intermediate levels.

Try to encapsulate low-level operations in some not-very-high-level
classes. For example, such encapsulation classes, or functions, do
all
pointer stuff, translate from error codes to exceptions, etc. Just
getting that bug-inducing low level stuff /out of the way/, packaged.

Else-thread I have already mentioned another aspect of that high
level
low level clash, that it would be a good idea to use std::vector
instead
of raw arrays and pointers."

So I'm trying to make this new version to address
these issues.
 
P

peter koch

But of course that's nonportable, meaning if one goes to a different
compiler with different libraries it may not happen. So is it really
a good idea to rely on it?
Of course it is! This is an aid in debugging, and if you suppose your
code to be portable, it should be fine to debug on one platform. Why
debug on more?

/Peter
 
M

mike3

Of course it is! This is an aid in debugging, and if you suppose your
code to be portable, it should be fine to debug on one platform. Why
debug on more?

Well, OK...
 
J

Jerry Coffin

[ ... ]
Hmm. This might work, however won't the calling
overhead to the operator[] be an issue? (I take
it you can't just tell the compiler to inline these.)
I'll have to bench it and see how fast it goes.

You don't have to tell the compiler, it will (or at least should!)
inline such trivial methods.

So if my compiler doesn't inline that, then that
doesn't indicate poor code, it indicates a poor
compiler, which I should throw out and substitute
a better one for, right?

When you place the definition of a member function inside the definition
of its class, that implicitly declares the function inline. Even though
it's declared as an inline function, the compiler isn't required to
actually expand the code inline -- and when you turn optimization off,
most won't. Even when you turn optimization on, there's usually a limit
to how complex of a function the compiler will expand inline -- but I
can't remember the last time I saw a compiler that couldn't expand
functions this trivial inline (in fact, I'm not at all sure there's ever
been a C++ compiler that couldn't).
Obviously, looking at this there are no "perfect"
solutions (not a surprise, though.). This therefore
raises the question of whether any of the solutions
here are adequate for the purposes at hand.

If you really find these compiling to an actual function call (except,
as previously noted, when you turn optimization off) then I'd say your
previous statement was about right: it's time to find a decent compiler
instead. I doubt you'll find such a thing though.
The whole point of the routines I've been struggling
so hard to craft here was to try and minimize the
bug-susceptibility of my code, but I also need high
performance. Therefore, the question comes up of
what the best way is to code these core routines,
like the adder I posted. What is it?

My advice would be to try the code I posted. If you're really concerned
about what code is produced, look at the assembly output it creates or
profile the result -- but quite frankly, I'd make that a fairly low
priority, because I'm fairly sure it won't be a problem.
 
M

Michael Hull

mike3 said:
I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

You should probably find a decent implementation of the Standard
library.  Or just tweak yours.
For example, consider the piece of code at the end of this message.
Should I use a boundscheck in there to check whether or not the ranges
specified are in-bounds or out-of-bounds? But if I do, then it makes
that seemingly nice piece of code real messy and I don't like it so
much.

The implementation (and essentially the design) comes from the set
of requirements.  Do your requirements include checking bounds in
the release mode?  Can bounds be ignored by the users of your class
(even if it's you)?  If they can (and essentially allowed to) and
you want to make your program robust enough to withstand those
"attacks" on the boundaries, then you _have_to_ check them yourself.
If you don't want to check and instead need to rely on the client's
code to check the bounds, then you need to document that crossing
the boundaries has undefined behaviour (just like the Standard says)
and put the burden of dealing with UB on the client.

V

Hi,
Sorry I know this is slightly OT, but could anyone tell me how to turn
on the debug options when using glibc/gcc. I remember finding out
about them last year, but now can`t find the #define for the life of
me.
Thanks

Mike
 
J

Jerry Coffin

[ ... ]
But of course that's nonportable, meaning if one goes to a different
compiler with different libraries it may not happen. So is it really
a good idea to rely on it?

That depends. The library itself is quite portable, so if (for example)
you buy the full version of the Dinkumware library, you can use it with
just about any compiler you're likely to want to use. In a case like
that, relying on it is perfectly reasonable.

Likewise, if you use bounds-checking to ensure your own code is correct,
rather than the verify user input, then being able to use it during
development, even if not during deployment, isn't necessarily a major
problem at all.
Furthermore, how good do you think the implementation of the bignum
addition I showed in my original post is?

It looks reasonable, though a quick check shows that it does a LOT
better (about ten times faster) when 64-bit math is available on the
CPU, instead of simulated by the MS library. I haven't tried it using
long long simulated on another compiler, but I wouldn't expect a huge
difference (MS usually does things like this reasonably well...)
 
M

mike3

[ ... ]
Hmm. This might work, however won't the calling
overhead to the operator[] be an issue? (I take
it you can't just tell the compiler to inline these.)
I'll have to bench it and see how fast it goes.
You don't have to tell the compiler, it will (or at least should!)
inline such trivial methods.
So if my compiler doesn't inline that, then that
doesn't indicate poor code, it indicates a poor
compiler, which I should throw out and substitute
a better one for, right?

When you place the definition of a member function inside the definition
of its class, that implicitly declares the function inline. Even though
it's declared as an inline function, the compiler isn't required to
actually expand the code inline -- and when you turn optimization off,
most won't. Even when you turn optimization on, there's usually a limit
to how complex of a function the compiler will expand inline -- but I
can't remember the last time I saw a compiler that couldn't expand
functions this trivial inline (in fact, I'm not at all sure there's ever
been a C++ compiler that couldn't).
Obviously, looking at this there are no "perfect"
solutions (not a surprise, though.). This therefore
raises the question of whether any of the solutions
here are adequate for the purposes at hand.

If you really find these compiling to an actual function call (except,
as previously noted, when you turn optimization off) then I'd say your
previous statement was about right: it's time to find a decent compiler
instead. I doubt you'll find such a thing though.
The whole point of the routines I've been struggling
so hard to craft here was to try and minimize the
bug-susceptibility of my code, but I also need high
performance. Therefore, the question comes up of
what the best way is to code these core routines,
like the adder I posted. What is it?

My advice would be to try the code I posted. If you're really concerned
about what code is produced, look at the assembly output it creates or
profile the result -- but quite frankly, I'd make that a fairly low
priority, because I'm fairly sure it won't be a problem.

OK, then, I'll do it.
 
E

Erik Wikström

mike3 said:
I just found out that the iterators used by std::vector<> are not
required to perform boundschecking, even as a debug mode! So then what
do I do? I'd like to have it available as a debug, even if not in a
release version.

You should probably find a decent implementation of the Standard
library. Or just tweak yours.
For example, consider the piece of code at the end of this message.
Should I use a boundscheck in there to check whether or not the ranges
specified are in-bounds or out-of-bounds? But if I do, then it makes
that seemingly nice piece of code real messy and I don't like it so
much.

The implementation (and essentially the design) comes from the set
of requirements. Do your requirements include checking bounds in
the release mode? Can bounds be ignored by the users of your class
(even if it's you)? If they can (and essentially allowed to) and
you want to make your program robust enough to withstand those
"attacks" on the boundaries, then you _have_to_ check them yourself.
If you don't want to check and instead need to rely on the client's
code to check the bounds, then you need to document that crossing
the boundaries has undefined behaviour (just like the Standard says)
and put the burden of dealing with UB on the client.

V

Hi,
Sorry I know this is slightly OT, but could anyone tell me how to turn
on the debug options when using glibc/gcc. I remember finding out
about them last year, but now can`t find the #define for the life of
me.

I think you use the -g option.
 

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,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top