11

I am currently adding handling to a segment of code so that it will not crash. Currently it has each step then an ASSERT statement to make sure nothing went wrong in the previous step. If something went wrong in one of these steps something did go very wrong. The program should stop.

Though in release mode the program hits a assert, happily carries on its way and crashes.

To fix this I have wrapped the method in a try/catch block and throw errors where the asserts used to be. This should catch all the errors we track and others we do not.

Now my question is, should I still be using asserts to notify the programmer that this should not have happened? Or take them out now that it will not crash because of the catch block (where I cleanup the object)?

Or alternatively should I just throw an assert in the catch block instead of with each throw statement?

marsh
  • 2,592
  • 5
  • 29
  • 53
  • Why are you catching asserts? Asserts should be used to check assumptions about your code, not for general error handling. – Colonel Thirty Two Mar 10 '15 at 20:57
  • I'm reading this as "we used try catch blocks to debug a problem that we couldn't reproduce in house." If you have fixed the problem and are now deleting these try/catch blocks, then you should first have a reproduction in your unit tests, and less importantly, I would put asserts back in where you delete the try/catch (not that they ever helped, apparently). – Kenny Ostrom Mar 10 '15 at 21:09
  • Thank you for all the answers, I do believe that I misinterpreted the assignment and you guys are quite right. I do not think I was meant to capture the existing assert errors. As explained they are a different beast entirely. – marsh Mar 10 '15 at 21:25
  • Ok, so if I leave all the asserts in place and add a try catch to recover from any other errors is it ok to throw my own errors? For example if I call Initialize() on another class. It doesnt throw but returns that it did not Initialize correctly. I can handle this with a bunch of if statements to cleanup and return. But since this happens a good deal in my constructor this would get messy. Is it appropriate to just throw a error myself so that the catch cleans it up? Or is throwing reserved bigger problems? – marsh Mar 11 '15 at 14:36

5 Answers5

26

try & catch and assert has completely different purposes (at least in my view).

try and catch are used to cope with expected error conditions (user supplied a filename that doesn't exist, calling new couldn't allocate memory, user entered invalid input of some sort, etc).

The purpose of assert is the guarantee that the programmer doesn't make mistakes. Hopefully, when running the code in release, you'd have already covered those alternatives, and know that the code is "good". Typical examples of assert would be some pointer the user should not supply as NULL is indeed not NULL, or a linked list has the expected number of nodes [e.g. you count the number of nodes before remove_node, and check that the number of nodes is indeed exactly one less].

If you are NOT 100% certain (or at least 98.75% certain or whatever level you decide is "good enough") that you have tested all of your code, then you should not make the release - or if you do, get your quality assurance manager to sign off that "we haven't done enough testing, but we are OK with that".

try and catch should only be used for things your program can reliably recover from. Not to catch "programmer wrote stupid code that causes a crash".

Edit to clarify the actual answer:

In other words, yes, you should use assert for things that you don't expect to ever happen in the code [at least if the programmer is not doing things wrong] - this includes for example checking that the vector inside a vector for a matrix is indeed a square when you expect to have a square matrix, for example, or that pointers are not NULL [except where you expect them to be - and perhaps even check that they ARE NULL when they SHOULD be].

You should also use error checking with try/catch and throw when things go wrong that could well happen during the running of the actual program in "real life" - disk full (or read-only), files not existing, not enough memory, things of that sort.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
3

I think you use assert macros, which are excluded when in release mode, thats why your code runs happily through them. They are mainly used for a kind of debugging, during developement to make sure your program works well.

Think of them like they are saying:

this should never happen here, if it happens there's a bug in my code, so I have to fix it

They tell you wheter you've made a (logic) mistake.

For example when you check for zero divisor:

int x = GetIntFromInput();
if (x != 0);
{
    assert(x != 0);
    float y = 10.0f / x;
}

This code has a sematic error, the ; behind the conditional. This assert will warn you during debugging/developement build.

Just use try/catch blocks if you want to create 'run-time' asserts, however I don't really like to throw them. Why should I not wrap every block in "try"-"catch"?. Maybe you can replace the exceptions with if-s or error codes.

Community
  • 1
  • 1
David Szalai
  • 2,363
  • 28
  • 47
3

Mats Petersson is quite right on assert. It's use is pre-release to check your internal logic. I won't repeat that point.

What I'd add, however, is let's suppose you want to check something else - for instance, check the result of something that should never go wrong, but might, for instance due to a problem in some other part of the system. In this instance, you should check for it (not using assert), and handle the situation gracefully. Normally the thing to do is throw an exception here, as you don't (normally) want the program to stop dead with no errors. Alternatively the thing itself with throw an exception. Often you want to carry on in a clean manner, but if you do want to stop, you almost certainly want to do some degree of clean-up first. That's what your catch block etc. is meant to do.

abligh
  • 24,573
  • 4
  • 47
  • 84
2

Defensive programming consists of defining your assumptions at each line of code, and still checking them.

An Assert should explain "at this point, I honestly believe that this condition failing would be a sign that the system is in a state not expected by the programmer (me)".

Having the code then also check for the failure of the assumption, and early exit, is an example of defensive programming. But those failures should only occur in special-crafted unit tests that break the preconditions of various interfaces (with asserts disabled). If it happens in normal unit tests, or in normal use, or on user systems, then you need to move the protection against failure somewhere else, or make the failure an expected failure mode.

Depending on the severity of the assert, you should resist the temptation to "limp on" if things are going extremely wrong. State of the program should be backed up in a way that it could be restored, the user should be notified and encouraged to shut the program down. If your program can do irreversible operations, a forced shut down is more reasonable.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

I have just realized that the question asked is not being answered. Rather than another lecture on what asserts and exceptions should and should not do, I'll address this: "Now my question is, should I still be using asserts to notify the programmer that this should not have happened?"

The answer is LOGGING. This should trigger an error log, which will be easily visible at the customer site, or at least to any of your people doing tech support.

Kenny Ostrom
  • 5,639
  • 2
  • 21
  • 30
  • Asserts have no use in developement build, in the sense of macro asserts, since they are entirely skipped in dev build. So it is better to answer what are they used for, and how can they be used - I think. – David Szalai Mar 10 '15 at 21:21
  • Logging is not the answer to this question - logging is the answer to the question "What should I do to ensure that when a customer has a problem in my software that is in release mode, and I can't trivially reproduce the problem in my office?" – Mats Petersson Mar 10 '15 at 21:25