3

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.

Community
  • 1
  • 1
paz
  • 31
  • 2
  • 3
    If you take a non-const reference instead of a pointer, you don't have this problem. :-) – James McNellis May 29 '10 at 03:50
  • @JamesMcNellis - or perhaps the developer just blindly deref's a NULL pointer and invokes undefined behavior. Hopefully the runtime will do something bad but with all UB, you can't know for sure. – R Samuel Klatchko May 29 '10 at 06:45
  • @RSamuelKlatchko: Well, right. But at least it makes it much easier to explain why the caller is at fault (IMO). – James McNellis May 29 '10 at 13:40
  • 1
    As others said: C++ has a language feature to specify a non-null value (references). Use it! – adrianm May 29 '10 at 19:47

8 Answers8

11

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
  • 17,276
  • 9
  • 52
  • 70
3

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
  • 496,577
  • 130
  • 894
  • 1,212
2

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
  • 74,869
  • 16
  • 134
  • 187
  • Putting this in a function negates the most useful aspect of assert: telling you the expression, filename, and line number. –  May 30 '10 at 05:39
  • @RogerPate - good point. I'll rewrite that as a macro. – R Samuel Klatchko May 30 '10 at 06:45
  • 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. –  May 30 '10 at 06:54
1

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
  • 84,970
  • 20
  • 145
  • 172
1

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.

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
1

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
  • 34,554
  • 11
  • 80
  • 120
1

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.

  • 1
    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. – Puppy May 29 '10 at 16:59
  • 1
    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 May 29 '10 at 17:07
  • @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 May 30 '10 at 03:54
0

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
  • 31,094
  • 6
  • 92
  • 116
  • 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 Jul 24 '10 at 17:05
  • 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 Jul 25 '10 at 04:03