evildave wrote:I agree that there are many ways to skin a cat, but...
There is no such thing as an 'optional' error.
Yes there are. As an example, I might call a function that could fail, but not care if it fails. So long as it doesn't break the program state and leaves on failure.
In that case, I would call the function with a null error pointer.
If you discover that this 'optional' error pointer is being initialized and passed into things on the stack EVERYWHERE, you might consider making it a (gasp!) 'global', or as part of the structured dice state. It'll be there because the app loaded.
Thread-local would be better, but even that isn't ideal. I want to give the client explicit control over what to do with errors, when to handle them, when to ignore them, and I prefer my functions to be as "functional" as is reasonably possible (ie, minimize side-effects).
In C++, you can do a significantly better job of the error parameter because of the automatic code generation abilities of it's struct/class syntax.
You could wrap that with a 'GetDiceError()' function in the library and hide the existence of that global, and not have to initialize it and pass it around everywhere. Then the caller can check for errors or perilously ignore them as they wish.
This does a number of bad things.
First, it introduces global state to your program. Global non-thread-local global state. Global state that is read and written in multiple locations throughout your code base.
That's pretty bad.
On top of that, it extends the interface of your function in a custom way. Instead of a simple pattern that can be worked out from the interface of the function, we now have a collection of 3 functions which interact using implicit connections between them.
In this particular case, I still maintain there's no reason to fail at all. The only true failure possible is a memory allocation failure, and I already went into how to make those failure tests go away. The other one's a parameter parsing issue, and that might be better dealt with long before calling the die roller.
The RNG state could be set up wrong.
If you are using a entropy-based RNG, you could run out of enthropy to generate new RNGs.
The error pointer is does not just only exist to be set: if the error pointer points to true, no code will be executed by a function that is called. This lets you write out a bunch of code and have it stop doing stuff at the moment the first error occurs.
One can do this using exceptions, but exceptions have their own problems: they are implicit, they can't be signatured correctly in C++, they cause unpredictable code-flow, they require careful use of RAII, and they don't work very well in C.
Writing code that can not possibly fail is writing robust code.
Writing code that has failure built into it is not writing robust code.
Assuming code cannot fail is not a robust code practice. Code that is robust is code that deals with failures in a well defined and tidy manner.
Every failure that is ignored is a possible code path quirk that has to be understood, possibly tested, and delt with.
Note that the error being set to true is optional. The code should seek to behave as well as it can, but when it is given garbage it can notify the calling function
as well as behaving as well as it can.
Yes, the C runtime library returns some NULL pointers in certain cases, and those need to be handled. Yes, many runtime libraries accept a 'NULL' as parameter meaning 'default this'. NO, you do not have to follow this terrible practice in your own code. NULL means 'bug', just as a pointer to '43' is a bug, or any pointer outside your normal addressing space is a bug.
This isn't consistent with the C standard, in which NULL is a well defined pointer. A pointer outside of your memory space isn't well defined in it's operation.
You can treat NULL pointers as bugs: that is an acceptable idiom. Claiming that any other idiom about NULL pointers is WRONG is ridiculous. :)
Speaking of ridiculous, vi or emacs? How many characters wide should tabs be? Do you put {} on the line-of the if, or on the next line?
I'm just curious what other religious beliefs you hold. :)
You shouldn't be passing around bad pointer values as a matter of course. A pointer is NOT a scalar or an enumeration, no matter how many people pretend it is.
No, pointers are not scalar values. I'm not using them as scalar values. I'm using them as a reference to storage that can refer to "nothing" optionally in one case, and in the other case as a reference to storage that
must exist.
This being C code, I can't distiguish between them strongly in the language syntax, so I am forced to distinguish between them using convention.
Besides, you know any code you post here, no matter how trivial, is going to get an 'anal probe'. To be fair, I suppose I could post something 'truly horrific' and defend it to the bitter end....
Sure, you are free to put forward your religious beliefs about coding. I, however, am under no obligation to tolerate your religion.
I claim there is more than one idiom. They have advantages and disadvantages.
What I displayed above was a defensively coded implementation using an error-handling idiom that I have seen used usefully, that I have used usefully, and that generates some nice properties that other error handling idioms lack.
It isn't the be-all the end-all error handling idiom, but it is still a useful one.
Regardless ofy your personal religious beliefs.
If you want to convince me, you could try not argueing from a religious fanatic standpoint. :) If you want to amuse me, continue.