473,385 Members | 1,901 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,385 software developers and data experts.

Handling class invariant violations

Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way to
handle it?

1) assertions while in debug mode (if release mode let caller suffer the
consequences)?
2) throw an exception?
3) return an error code?
4) other?

In all cases I would leave the value of Foo::bar unchanged. The question is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid to use
exceptions to invalidate input or is that an abuse of exception handling?

I am leaning towards solution #1.

Any help would be appreciated.

Brian
Jul 22 '05 #1
8 1940
"DaKoadMunky" <da*********@aol.com> wrote in message
news:20***************************@mb-m04.aol.com...
Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even
number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way
to
handle it?
Your example is so artificial that it doesn't give enough context to answer
this question.

1) assertions while in debug mode (if release mode let caller suffer the
consequences)?
2) throw an exception?
3) return an error code?
4) other?

In all cases I would leave the value of Foo::bar unchanged. The question
is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid
to use
exceptions to invalidate input or is that an abuse of exception handling?
If calling SetBar with an odd number is an error which could be caused by
user input I don't see why throwing an exception is wrong. Remember, the
caller may be able to fix the problem with the information you provide in
the class you throw. However, this cannot really be decided in the abstract.

I am leaning towards solution #1.
That could be right, particularly if the integer argument can only be the
programmer's fault, not the client's fault. Hopefully you can test the code
enough to flush out any such logic errors. However, if the user input can
lead to this condition this approach is probably wrong. I'll bet you are a
client for software yourself -- if you put in inconsistent input would you
like an explanation of how to fix it or watch helplessly as the program
crashes?

Any help would be appreciated.
I would add that exceptions are better than error codes in most cases.
Extensive use of error codes makes the calling software look like a sequence
of error checks, and it becomes easy to lose the thread of what is actually
going on when there are no errors. I use error codes sometimes, usually when
I am implementing a C interface to a DLL or something like that.

Brian


--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #2
"DaKoadMunky" <da*********@aol.com> wrote...
Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even
number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way
to
handle it?

1) assertions while in debug mode (if release mode let caller suffer the
consequences)?
2) throw an exception?
3) return an error code?
4) other?

In all cases I would leave the value of Foo::bar unchanged. The question
is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid
to use
exceptions to invalidate input or is that an abuse of exception handling?

I am leaning towards solution #1.


First, let me note that this is not a language problem. This is a software
engineering problem, or perhaps a library design problem. In any case, do
what you feel is the requirement of the users of the function.

Second, since you asked, I'll say where I lean. If you allow the user to
pass in _any_ value (as implied by the type and the name), throwing is not
the right thing, since no value is an _exception_. Debug-time assertions
are only good if you make sure your test runs cover _all_ cases, so you
only use assertions to find an error in the code that calls your function.
If you release your function to the user, assertions will not help. Error
codes are good if your user is going to actually verify them.

So, considering all that, none of your 1-4 suggestions would work. What to
do, you might ask. There is no fool-proof solution here, unfortunately.
I would probably implement the function as

void SetBarToEvenPartOf(int i) { bar = (i/2)*2; }

and document the hell out of the fact that calling that function with 3 will
actually set bar to 2.

V
Jul 22 '05 #3
DaKoadMunky wrote:
Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way to
handle it?
1) assertions while in debug mode (if release mode let caller suffer the
consequences)?
2) throw an exception?
3) return an error code?
4) other?

In all cases I would leave the value of Foo::bar unchanged. The question is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid to use
exceptions to invalidate input or is that an abuse of exception handling?


I believe exceptions is the only solution. If you document the fact the
SetBar should only be called with even number, calling it with odd
number is an exceptional situation. Asserts are generally used to catch
bugs in your code, not someone's else's code. Error codes is an option,
but exceptions look cleaner.

-Arijit
Jul 22 '05 #4
"DaKoadMunky" <da*********@aol.com> wrote in message
news:20***************************@mb-m04.aol.com...
Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even
number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way
to
handle it?

1) assertions while in debug mode (if release mode let caller suffer the
consequences)? Is the risk worth taking?
What are the possible consequences of storing an invalid value?
Can you be sure that debug-mode testing will catch all the possible errors
in all programs that use your calss?
Can you exclude the possibility that the value is obtained from user
input, or from a source file that might be corrupted?
I'd much prefer to detect and report the error in such cases -- especially
if a broken class invariant could later trigger undefined behavior...
2) throw an exception? Yes.
Indeed, today it is not uncommon to replace the classic 'assert' with
a call that throws an exception (derived from std::logic_error) even in
release mode - at least when checking preconditions/inputs of a function.
You could even define a new "macro" dedicated to this (e.g. PRECONDITION).
3) return an error code? Knowing that most users tend to disregard retuned status codes?
No. This is what exceptions are for.
4) other? Automatically "fix" the input, or choose a default behavior?
This can be an acceptable choice, if there is a reasonable choice for a fix.
This could then be combined with a warning check (which would act like
assert in debug mode, and either do nothing or log an event in release
mode).
I sometimes use a "WARN_IF" macro for such cases, defined as something like:
#define WARN_IF(b) ((b)&&(IMPL_show_warning(#b))

For example:
void SetBar(int i) { WARN_IF(i&1); bar = i&~1; };
or:
void SetBar(int i) { if(WARN_IF(i&1)) i=0; bar = i; }
In all cases I would leave the value of Foo::bar unchanged. The question
is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid
to use
exceptions to invalidate input or is that an abuse of exception handling? Definitely not an abuse.
std::logic_error, as I understand it, is designed as a base class for
such exceptions.
I am leaning towards solution #1.

Based on past experience, I (personally) strongly recommend #2 or #4.
Regards,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form

Jul 22 '05 #5
> Throwing an exception seems like the least likely solution. Is it valid
to use exceptions to invalidate input or is that an abuse of exception
handling?


Why? Of course it is. There's even a pre-defined exception called
std::invalid_argument (which is of type std::logic_error).
It's used to throw on exactly those occasions, in fact if the client (be it
programmer or user) has passed a faulty value on which your code can't
operate.

Well, one might argue if this situation is "exceptional". That however
depends on your code and the overall context.

Regards,
Matthias
Jul 22 '05 #6
If the parameter you pass must be an even number, then it isn't really an
int anymore - so I'd say your function specification is incorrect - I'd
define a class EvenNumber, and use that as an input -

void SetBar(const EvenNumber & )...

Ok this just defers the question to whether to throw an exception if a user
attempts to construct / assign an odd number to an instance of EvenNumber (I
would), but it's more obvious to users what is intended.

ad



"DaKoadMunky" <da*********@aol.com> wrote in message
news:20***************************@mb-m04.aol.com...
Say I have the following...

class Foo
{
public:

Foo() : bar(0) {}

void SetBar(int);

private:

int bar;
};

Say that an invariant for my class is that Foo::bar always be an even
number.

If somebody calls Foo::SetBar(int) with an odd number what is the best way
to
handle it?

1) assertions while in debug mode (if release mode let caller suffer the
consequences)?
2) throw an exception?
3) return an error code?
4) other?

In all cases I would leave the value of Foo::bar unchanged. The question
is
how and if to let the caller know the value was not changed.

Throwing an exception seems like the least likely solution. Is it valid
to use
exceptions to invalidate input or is that an abuse of exception handling?

I am leaning towards solution #1.

Any help would be appreciated.

Brian

Jul 22 '05 #7
* DaKoadMunky:

Please use at least a pronouncable nick (or your real name, best).

Say that an invariant for my class is that Foo::bar always be an even number.

If somebody calls Foo::SetBar(int) [which does nothing but assign the argument
value to Foo::bar] with an odd number what is the best way to handle it?


There is a mismatch between the interface you present to client code,
and the internal class invariant.

As it is, the caller of Foo::SetBar must use a redundant representation.

The simplest and best way to handle that problem is to decide whose job
it is to _reduce_ the redundancy to the required non-redundant
representation.

Is it your class? Then throw an exception (but contrary to advice given
elsewhere in this thread, don't use the standard's predefined exception
type for that, because it derives directly from std::exception; use
std::runtime_error or a derived exception class). Also then consider
adding a middleman argument type, e.g. EvenNumber, that can do this.

Is it the client code? Then change the interface. E.g. document that
SetBar will use twice the argument value (or whatever).

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 22 '05 #8
"Alf P. Steinbach" <al***@start.no> wrote...
* DaKoadMunky:

Please use at least a pronouncable nick (or your real name, best).


Why should he (or she)? Are you dictating your responses to your
computer?
[...]

Jul 22 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

50
by: Dan Perl | last post by:
There is something with initializing mutable class attributes that I am struggling with. I'll use an example to explain: class Father: attr1=None # this is OK attr2= # this is wrong...
12
by: Ritz, Bruno | last post by:
hi in java i found that when a method has a throws clause in the definition, callers must either handle the exceptions thrown by the method they are calling or "forward" the exception to the...
15
by: Steven T. Hatton | last post by:
The following may strike many of you as just plain silly, but it represents the kind of delelima I find myself in when trying to make a design decision. This really is a toy project written for...
4
by: MadSage | last post by:
I currently have a multi-threaded server application with worker threads and a core thread. For all of these threads I have something like the following code: uint32 __stdcall...
4
by: Rob | last post by:
Hey all, So.. a simple FormView/SqlDataSource to handle inserting records into a table. The table has a primary key that the user enters (eg DiscountCode). If the user enters a duplicate the...
6
by: Michael | last post by:
Hi, In real world, when to use class, container, class & container? It seems to me that container is more useful to number chain, class is for implementing complex porjects? Right? Please give...
35
by: jeffc226 | last post by:
I'm interested in an idiom for handling errors in functions without using traditional nested ifs, because I think that can be very awkward and difficult to maintain, when the number of error checks...
5
by: John | last post by:
Hi I have developed the following logic to handle db concurrency violations. I just wonder if someone can tell me if it is correct or if I need a different approach.Would love to know how pros...
6
by: asm23 | last post by:
Hi, everyone, I'm learning <<thinking c++>volume Two, and testing the code below. ///////////////////////////////////////////////////////////////////////////// //: C01:Wrapped.cpp #include...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.