views:

244

answers:

8

Possible Duplicate:
design by contract tests by assert or by exception?

What is the preferred way to handle a null pointer passed in as an output argument to a function? I could ASSERT but I feel like its not good to let a library crash the program. Instead, I was thinking about using exceptions.

+10  A: 

Throw an exception! That's what they're for. Then the user of your library can decide if they want to handle it gracefully or crash and burn.

Another specific solution is to return an invalid value of a valid type, such as a negative integer for a method returning an index, but you can only use that in specific cases.

Chris Cooper
@aaa: My mistake. I meant as an index. Thanks!
Chris Cooper
A: 

The benefit of using exceptions is that you let your client code decide how to handle the exceptional circumstance. That's for the case where the parameter being non-null is a stated precondition of the function. For functions taking optional out parameters, though, passing NULL can be an indication that the client is not interested in the value. Presumably, you're using the return value to signify success or failure, and if that's the case, you could simply detect the NULL and return an error code if the parameter is mandatory, or simply ignore it if the parameter is optional. This avoids the overhead of exceptions and still allows error handling on the client's part.

warrenm
In the days of exceptions, with languages that have them, no one checks error codes anymore. They just kinda assume an exception will be thrown if there's a problem.
cHao
That might be true in Java, which throws at the drop of a hat, but in languages that use exceptions more sparingly (Objective-C comes to mind), checking for errors is still essential for maintaining consistency and controlling flow.
warrenm
+1  A: 

If you value performance, assertions will be off in release. They're there to catch problems that should never happen, and shouldn't be used to catch stuff that may happen in real life. That's what exceptions are for.

But let's back up a second. Where is it guaranteed what will happen if you dereference a null pointer, whether writing to it or not? It may crash for you, but it won't crash in every OS, or every compiler, or any anything else. That it crashes for you is just good fortune on your part.

I'd say throw an exception if you're not gonna create the object yourself and have the pointer to the pointer passed to you, the way I often see 'out' params passed.

cHao
A: 

If you are programming the autopilot system for the ultimate airplane, I should recommend trying to handle the exception gracefully.

Please read the Eiffel specifications for "contract programming" (a very nice language indeed) and you'll be enlightened. NEVER crash if you can handle the event.

belisarius
+3  A: 

Do both.

Any that can be caught during development will abort the process which will make it obvious to the developer that they need to fix it.

And if one does make it past testing, there's still the exception that a robust program can handle.

And this is easy enough to put into a macro (must be a macro and not an inline so that assert properly reports the line number - thanks to @RogerPate for pointing out this out):

#define require_not_null(ptr) \
    do { assert(ptr); if (!(ptr)) throw std::logic_error("null ptr"); } while (0)
R Samuel Klatchko
Putting this in a function negates the most useful aspect of assert: telling you the expression, filename, and line number.
Roger Pate
@RogerPate - good point. I'll rewrite that as a macro.
R Samuel Klatchko
Just make sure you put it on the duplicate (linked above) instead of here. I had thought about just posting such a macro, but I didn't want to deal with multiple evaluations of the expression, etc. (you end up having to rewrite assert itself, which isn't hard, but then it doesn't match the implementation's output format, etc.). *However,* looking at it again, since side-effects in an assert expression are **bad** anyway, this is only a performance concern.
Roger Pate
+1  A: 

if you throw, a client can decide to re-throw, or not handle the exception, or crash or call exit or try to recover or....

If you crash, the client crashes with you.

So throw, to give your client more flexibility.

tpdi
+1  A: 

I would neither raise an exception nor use assert, which is what the C++ Standard library does. Consider about the simplest function in the library, strlen(). If it raised an exception, how would you possibly handle it? And the assertions won't fire in production code. The only sensible thing to do is say explicitly that the function must not be called with a NULL pointer as a parameter, and that doing so will result in undefined behaviour.

anon
Possibly because strlen() existed well before exceptions. If strlen did raise an exception, you'd catch it with a try catch block, like any other exception.
DeadMG
I think i agree with Neil that `strlen` throwing an exception would be weird. What sense does it make to catch its exception? But the following statement doesn't preclude assertions at all (it neither precludes exceptions since undefined behavior can do anything it wants. But if the calling code has no clue whether something throws, throwing becomes pretty pointless since noone is prepared to catch it!): "The only sensible thing to do is say explicitly that the function must not be called with a NULL pointer as a parameter, and that doing so will result in undefined behaviour."
Johannes Schaub - litb
@JohannesSchaub-litb - one major use for exceptions is for multi-threaded servers. At the top of the request loop you `catch (...)` which gives up on that one request without taking down the entire server and affecting other requests. Now if an unusual edge case does cause the exception the server can continue handling all other requests without crashing.
R Samuel Klatchko
+3  A: 

I would use an assertion if null pointers are not allowed. If you throw an exception for null pointers, you effectively allow them as arguments, because you specify behavior for such arguments. If you don't allow null pointers but you still get them, then some code around definitely has a bug. So in my opinion it does not make sense to "handle" it at some higher levels.

Either you want to allow callers to pass null pointers and handle this case by throwing an exception and let the caller react properly (or let the exception propagate, as the caller wishes), or you don't allow null pointers and assert them, possibly crashing in release mode (undefined behavior) or use a designated assertion macro that is still active in release mode. The latter philosophy is taken by functions such as strlen, while the former philosophy is taken by functions such as vector<>::at. The latter function explicitly dictates the behavior for out-of-bound values, while the former simply declares behavior undefined for a null pointer being passed.

In the end, how would you "handle" null pointers anyway?

try {
  process(data);
} catch(NullPointerException &e) {
  process(getNonNullData());
}

That's plain ugly, in my opinion. If you assert in the function that pointers are null, such code becomes

if(!data) {
  process(getNonNullData());
} else {
  process(data);
}

I think this is far superior, as it doesn't use exceptions for control flow (supplying a non-NULL source as argument). If you don't handle the exception, then you could aswell fail already with an assertion in process, which will directly point you to the file and line number the crash occurred at (and with a debugger, you can actually get a stack trace).

In my applications, i always take the assert route. My philosophy is that null pointer arguments should be handled completely by non-exceptional paths, or asserted to be non-NULL.

Johannes Schaub - litb