|
c'mon, no option for "Dump core and let the user figure it out"? W0t!?!
shog
nine
Ever since i heard the voice
i thought i had no choice...
|
|
|
|
|
Eh, C++ isn't any better. (Actually worse). There we either just display a message box say "Program hosed, exiting" or we try to repair ourselves and half the time leave the program in a trashed state. I have had programs that would display an exception message box in their OnPaint. Every time you tried to exit the program, you would get another exception.
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
:: Jeremy waves his hands franticly. :: Here, here, over here!
Jeremy Falcon
Imputek
|
|
|
|
|
Sometimes I write libraries, and sometimes I use other libraries. So, we have two scenarios:
1) When I use other libraries, I want them to be "lean", and I'll take care about error-checking. STL is a great example of this.
2) When I write a library that others will use, I'll do all error checking (unless the requirements say otherwise), so that whoever uses the library cannot blame me for their incompetence.
|
|
|
|
|
I remember barely a few years ago hearing from C++ devs who prided themselves on writing lean and mean apps that did as little error and bounds checking as was necessary. Not to say that it wasn't done: rather, that it was done before a function call and then once the data verified the function would be called. The thought that the function they were calling would do further checking was considered by some to be not just a waste of cycles but also something only of value to lame programmers who still slept with their teddy bears used night-lights.
It's interesting to see a shift in focus from performance based programming to secure and robust based programming.
cheers,
Chris Maunder
|
|
|
|
|
Chris Maunder wrote:
rather, that it was done before a function call and then once the data verified the function would be called.
Which is fine, so long as you control both. But the use of 3rd party libraries requires the library to either be very well designed and documented (so validation *can* be performed ahead of time), or to be robust enough not to crap out when bad data is passed in.
shog
nine
Ever since i heard the voice
i thought i had no choice...
|
|
|
|
|
Being a C++ dev at heart, I feel I'm in a position to answer.
IMHO the poll is heavily biased towards fat interface APIs with little abstraction, where pointers to built-in types are passed around. It is in the C++ spirit to minimize the possibility of erroneous calls rather than detecting them by means of (at least) the following two techniques:- Data abstraction,
- RAII (Resource Acquisition Is Initialization) idiom.
Let me give a couple of examples about this:- Buffer overruns and dangling
char pointers can be a nightmare only if you use them. std::string greatly reduces these risks simply because they are not allowed. Here, data encapsulation ensures some degree of integrity before related pieces of information (like a buffer and its length). The richer the semantincs of a type, the less errors of this kind will happen. - The semantics of
CWnd s often force the programmer to go trough the sequence construction-creation-destruction. This is IMHO an open hole for errors cause the possibility exists of having a "void" CWnd with no attached HWND . If MFC guys had moved to RAII, CWnd s will be created as part of their constructor, elminating the possibility of the error described above. This render the common ASSERT(IsWindow()) s unnecesary. "void" CWnd s van still be modeled, should the need arise, by having unassigned CWnd pointers.
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
|
|
|
|
|
Joaquín M López Muñoz wrote:
The semantics of CWnds often force the programmer to go trough the sequence construction-creation-destruction. This is IMHO an open hole for errors cause the possibility exists of having a "void" CWnd with no attached HWND. If MFC guys had moved to RAII, CWnds will be created as part of their constructor, elminating the possibility of the error described above. This render the common ASSERT(IsWindow())s unnecesary. "void" CWnds van still be modeled, should the need arise, by having unassigned CWnd pointers.
I think that you are right about RAII but in that particular case, I'm not sure of what should be the solution since allowing no attached HWND allows so simplification of the code and help for RAD...
The C++ solution (and Delphi too) is that the object would hide the fact that the object is created or not by esentially duplicating all properties (for ex. Text, Position,...) so that you can alter them even when the object is not yet created... It works relatively well. I think that .NET Forms would be similar in that area.
Maybe a good solution would be to hide a bit more the user interface by access the data through a class that would knows if a interface object is connected to it (so that the UI is updated when created)... But IMHO, such a solution is probably too complicate for the benefit.
But OTOH since MFC uses DDX for data exchange, maybe it would have been possible to uses RAII exclusively... but then we would have need the possibility to transfer far more property... and also MFC update everything at once and it would cause a lot of overhead...
Philippe Mori
|
|
|
|
|
Chris Maunder wrote:
It's interesting to see a shift in focus from performance based programming to secure and robust based programming.
I think there is a middle ground for C and C++ programmers, a way to have it both ways (almost). My approach, which I've just recently adopted, is to use assertions to validate preconditions. That way when the code is finished, those tests do not affect the performance of the final product. Exceptions come into the picture when a function cannot perform its duty after the preconditions have been verfied through the assertions. In those cases, an exception is thrown.
This is not a full-proof method because it is possible to imagine certain situations in which the UI passes along data from the user that is invalid in production code. The assertions are no longer there at that point, so you have trouble. However, this is the UI's responsibility and not the libriaries; validating user input is up to the UI. As long as the preconditions are clearly documented, the library is no longer responsible for these kinds of bugs.
I think the use of assertions for validating preconditions is a good middle ground - Defensive programming without driving yourself nuts.
|
|
|
|
|
*wave arms* I'm here!
But I do make a difference between "ordinary code" and security related code.
In Debug-builds I [ATL]ASSERT() anything that moves. ASSERT() is a friend you can trust.
--
Chatai. Yana ra Yakana ro futisha ta?
|
|
|
|
|
Robustness first, performance second.
I've always lived by that rule, unless the boss forces me to do otherwise.
Most performance implementations are based on assumptions about the code. That's all well and good until you get some hotshot programmer that breaks all your assumptions and tears down that house of cards you call an application.
------- signature starts
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
Please review the Legal Disclaimer in my bio.
------- signature ends
|
|
|
|
|
Data needs to be checked whenever it comes in from a source you need to trust but can't.
A library should be made to trust the users of the library, except when this compromises security. If anyone can use the library, and invalid data causes a security problem, you should check the data in the library.
If the only consequence is that the calling app will die, there's no reason for the library to waste time checking stuff. A program determined to crash will crash.
Now, the debug build should check everything, probably at multiple layers.
|
|
|
|
|
I'm another long time C/C++ guy who strongly believes in robustness over performance. The average user, whether an end-user or a programmer, is going to be far more forgiving of a few nano-seconds processing time compared to waiting weeks or months to get a bug fixed.
"Any clod can have the facts, but having opinions is an art."
Charles McCabe, San Francisco Chronicle
|
|
|
|
|
I think it comes down to adequate documentation and understanding thereof. If the user has been fully informed in how to use a function/class/API correctly, I have no problem leaving out the extra checking on the library side. OTOH, when the library makes further calls into itself or into the system API or wherever, it then has the onus of checking what it is passing on.
|
|
|
|
|
Chris Maunder wrote:
The thought that the function they were calling would do further checking was considered by some to be not just a waste of cycles but also something only of value to lame programmers who still slept with their teddy bears used night-lights.
In my opinion, the question revolves around the source code license. If you provide the code to your library, and are willing to allow customers to change it for their own need, you can choose to do less error checking. Ideally, make it a compile-time switch to turn on/off data validation.
If you provide only compiled libraries, particularly DLLs, then you must do robust error checking. If you don't there is too large a liability for the customer.
c
|
|
|
|
|
If you have to validate everything because you can't trust a library function, then you you probably are not saving much effort from using the library in the first place and are probably better off just implementing your own version of the function.
JMHO
|
|
|
|
|
..now that depends on what the library call is. Even if you could implement your own version; would you want to spend the time working out how to actually implement it? Probably not, thats why you choose to use a library in the first place!
=)
|
|
|
|
|
you're right. I would probably would look for an alternate library instead. I would not use a library that doesn't check for invalid parameters.
|
|
|
|
|
Well, not every check can be made by the library itself.
But I think most of the check should be placed in the library.
|
|
|
|
|
Yes, if you want to use one of the advantages of library funcion, you must trust in that library and save your time. If the library function writer was make the validation of all input data why to make your own validation?
CC
|
|
|
|
|
If the library function should validate the data, how thorough would you be? If a pointer is sent to the function, would you use IsBadReadPtr , IsBadWritePtr and IsBadStringPtr to validate the pointer or would you simply check if the pointer isn't NULL ? If the pointer in turn is a pointer to a struct would you in turn validate all its pointer members with IsBadReadPtr , IsBadWritePtr and IsBadStringPtr , and all its member functions with IsBadCodePtr ?
A lot of responsibility lies upon the caller, most of the time the callee must trust the caller with the data it sends.
If the documentation of the function states that a parameter must not be NULL (or that it must be valid) it is up to the caller to see to that it really is (or else I will call abort ).
|
|
|
|
|
You don't design robust operating systems by assuming the caller passing in good data. That would give you Windows 95/98.
Tim Smith
I'm going to patent thought. I have yet to see any prior art.
|
|
|
|
|
True, robustness doesn't come hand in hand with the assumption that the caller will always be passing good data.
Robustness is not always what you are looking for. In operating systems, yes. In games, not always, especially if it hurts the FPS and the rendering quality.
If the caller will send erronious data to a function, it will be the callers fault if the program crashes. The callee could do anything in its power to prevent it, but still, it would be the caller's fault that the program crashed. Ever called delete on a pointer that you already have delete d, what happend then?
Open Source software could help this by doing assert s (and similar things in debug builds) to notify the programmer that he/she did something wrong. But in release builds of a game, these checks would hurt the FPS.
It all depends on what you are developing.
|
|
|
|
|
Good documentation also helps, if I write something like "the results are undefined if the X parameter is NULL or if it does not point to valid data" in the documentation for a function and the caller does so anyway, I'll gladly let the program crash, it is not my problem, not by a longshot.
If my library is used in a heart-lung machine, and the patient dies, it is not my problem (unless I'm the patient), but not my fault.
If my library is used in a space shuttle, and the shuttle crashes, it is not my problem (unless I'm one of the seven), but not my fault.
If my library is used in a nuclear warhead, and it somehow detonates and WW3 starts and all people die, it is my problem, but not my fault.
|
|
|
|
|
ASSERTS are a BAD idea.
I'd rather write the code correctly than use ASSERTS. What if the person calling your library function doesn't have the source code to it, and your code ASSERTS for some reason?
Dalle wrote:
It all depends on what you are developing.
What a crock of horseshit. Crap design = crap performance.
------- signature starts
"...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
Please review the Legal Disclaimer in my bio.
------- signature ends
|
|
|
|