Thread overview | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
July 31, 2010 Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
This part has always bothered me. Could somebody please explain to me the rationale behind limiting functions to one usable error code? if(function()) ~~ :Pluto |
July 31, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pluto | On 31.07.2010 23:44, Pluto wrote: > This part has always bothered me. Could somebody please explain to me the > rationale behind limiting functions to one usable error code? > > if(function()) > ~~ > Inherently if is for testing _condition_ which is true/false. Going futher you'd just reinvent switch statement. which if perfectly OK for it, here it goes: switch(function()){ case ERR_CODE1: // --- break; case ERR_CODE2: // --- break; // --- default: // --- break; } Honestly, I'd suggest using exceptions instead of error codes. Usage of error codes scales poorly and itself is very error-prone, also killing the return value of functions just for error code leads to very awkward design. > :Pluto > -- Dmitry Olshansky |
July 31, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pluto | Pluto <pluto@planets.not> wrote: > This part has always bothered me. Could somebody please explain to me the > rationale behind limiting functions to one usable error code? Well, traditionally it was done because testing for 0/non-0 is a simple and fast operation. Also, boolean logic can be thought of as simple maths, as AND is multiplication and OR is addition. This only makes sense if false == 0. As for having more error codes, why would you use an int for it? Would it not be more logical to use an enum, and to explicitly test for the value upon return? Why not use exceptions? If the problem is that enums are non-extensible, and exceptions are too heavy, many libraries use the convention that 0 means success, and anything else is an error code. -- Simen |
July 31, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | == Quote from Dmitry Olshansky (dmitry.olsh@gmail.com)'s article > On 31.07.2010 23:44, Pluto wrote: > > This part has always bothered me. Could somebody please explain to me the rationale behind limiting functions to one usable error code? > > > > if(function()) > > ~~ > > > Inherently if is for testing _condition_ which is true/false. Going > futher you'd just reinvent switch statement. > which if perfectly OK for it, here it goes: > switch(function()){ > case ERR_CODE1: > // --- > break; > case ERR_CODE2: > // --- > break; > // --- > default: > // --- > break; > } But these aren't compatible for the same function. Defining false as <1 would fix this. > Honestly, I'd suggest using exceptions instead of error codes. > Usage of error codes scales poorly and itself is very error-prone, also > killing the return value of functions just for error code leads to very > awkward design. I was asking purely out of design interest. ~~ :Pluto |
July 31, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen kjaeraas | == Quote from Simen kjaeraas (simen.kjaras@gmail.com)'s article > Pluto <pluto@planets.not> wrote: > > This part has always bothered me. Could somebody please explain to me the rationale behind limiting functions to one usable error code? > Well, traditionally it was done because testing for 0/non-0 is a simple and fast operation. So speed it is. Was <1 really slower back then? > Also, boolean logic can be thought of as simple maths, > as AND is multiplication and OR is addition. This only makes sense if > false == 0. false < 1, is what I would expect. It even makes it more symmetrical. > As for having more error codes, why would you use an int for it? Would it > not be more logical to use an enum, and to explicitly test for the value > upon return? Why not use exceptions? > If the problem is that enums are non-extensible, and exceptions are too > heavy, many libraries use the convention that 0 means success, and > anything else is an error code. Don't worry, I won't start using return types like this in D. Just interested. |
July 31, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pluto | On Saturday 31 July 2010 16:22:46 Pluto wrote:
> But these aren't compatible for the same function.
> Defining false as <1 would fix this.
Historically, false has always been 0 and true non-zero - probably because it's then easy to use the assembly instruction to check whether something is 0 (which I believe is cheaper than checking for a specific value).
In any case, changing that would likely break both people's expectations and their code. And regardless of that, it _isn't_ an error code to have a function return whether it succeeded or not. If you want to check error codes, then check for specific values or actually use the returned value rather than just throughing it in a condition by itself.
_Every_ type converts to bool when put in a condition statement. It's entirely consistent that way. And having 0 be false and 1 true for numeric types is entirely consistent with how things have historically worked and pretty much everyone will expect them to work. Changing that would cause far more problems than it would ever solve. If you want error codes, you can just check the return value against 0 like has historically been done and you're fine, but error codes are generally a poor choice anyway.
- Jonathan M Davis
|
August 01, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pluto | Pluto <pluto@planets.not> wrote: > == Quote from Simen kjaeraas (simen.kjaras@gmail.com)'s article >> Pluto <pluto@planets.not> wrote: >> > This part has always bothered me. Could somebody please explain to me >> the >> > rationale behind limiting functions to one usable error code? >> Well, traditionally it was done because testing for 0/non-0 is a simple >> and fast operation. > So speed it is. Was <1 really slower back then? Likely not. But the assembly is easier: jnz <label>; vs cmp EAX 0; jg <label>; >> Also, boolean logic can be thought of as simple maths, >> as AND is multiplication and OR is addition. This only makes sense if >> false == 0. > false < 1, is what I would expect. > It even makes it more symmetrical. How? Let's start out with a false value f of -1. Now AND it with itself (f * f), and you get 1, which is true. This does not seem correct. -- Simen |
August 01, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | == Quote from Dmitry Olshansky (dmitry.olsh@gmail.com)'s article
> Honestly, I'd suggest using exceptions instead of error codes.
This seems to be an increasingly common but still expensive suggestion for error handling. Unless D has made great strides in exception performance (which is entirely possible), I still believe that, where performance is anything like an issue anyway, exceptions should be used only for truly exceptional condition--not alternate flow that could/should be expected.
But I'm not just being a curmudgeon; I also have a suggestion. If you really want to take different paths based on a function return, make that return an object. This is what exceptions do, but it need not be so expensive. You could have a generic return class that has a string, a value, and casts to bool to give you most of what exceptions and bool return types give. But you can also sub-class that class and add polymorphic behavior to avoid the switch on value. Or make it a singleton and not pass it back. Or have one for each important call path, to which every called function can add delegates to run onError() or run deferred at some root level.
Some of this would be a lot of work to setup, but I find it pretty generic. I should probably make a library and post it. For the stuff that most folks use exceptions for (i.e. to hold a string,) this class is easy to write, and costs must less at run-time.
I will freely allow the critique that if do you this, then you'd have to check the return object after each call to see if there's error handling required. But I see this as a chief benefit for the maintainer (usually me in 6 months after I've forgotten everything about the code.) It spells out that there is another code path that's important to consider when changing things. Letting exceptions go by tends to obfuscate that. For out of memory, sure throw and don't catch. But for input string didn't parse, you want to handle that safely, informatively, gracefully, or whatever your robustness requirements are. So make it clear to someone that it needs to be handled.
I see a lot of exception-unsafe code. I think programmers often don't appreciate that your function may suddenly return between any two arbitrary function calls, and almost between any two arbitrary statements. You have to really design carefully for exceptions. So it turns out this problem is a tough one and requires effort no matter what.
Just a thought.
Jason
|
August 01, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jason Spencer | On Saturday 31 July 2010 20:15:58 Jason Spencer wrote: > == Quote from Dmitry Olshansky (dmitry.olsh@gmail.com)'s article > [...snip] > > Just a thought. > Jason I honestly don't think that exception handling is a particularly expensive solution in most cases. When dealing with an error, it's likely to either be related to I/O (be it an actual I/O error or input from a user or file) or a logic error in the code. For logic errors, efficiency isn't really an issue, since they shouldn't be happening. If they are, go fix your code and it won't be an issue. For I/O-related errors, I/O is already slow. You can't expect that to be efficient anyway. And if you're get an I/O error, you have a problem that has to be dealt with, and efficiency probably isn't an issue at that point anyway. The main issue with exceptions is whether code handles them correctly, and that's just as true of error codes. Particularly when you add RAII and scope into the mix, it's really not all that bad. There's always the issue over checked exceptions with there being pros and cons both ways, but overall, I'm definitely pro-exceptions and anti-error handling. I really do think that it makes for better code and that efficiency really isn't an issue in most cases. Maybe if you're dealing with embedded code, it might matter. But beyond something like that, exceptions will be better. As for how to use exceptions, I really think that Java does it quite well and is a good role model overall. It's just the checked exceptions that are particularly debatable. Your suggestion isn't necessarily bad, but I really don't think that there's anything wrong with using exceptions and that having to deal with errors explicitly like you would with error codes or your suggestion just clutters code and makes it less maintainable. - Jonathan M Davis |
August 01, 2010 Re: Casting an expression to bool means testing for 0 or !=0 for arithmetic types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen kjaeraas | == Quote from Simen kjaeraas (simen.kjaras@gmail.com)'s article > Pluto <pluto@planets.not> wrote: > > == Quote from Simen kjaeraas (simen.kjaras@gmail.com)'s article > >> Pluto <pluto@planets.not> wrote: > >> > This part has always bothered me. Could somebody please explain to me > >> the > >> > rationale behind limiting functions to one usable error code? > >> Well, traditionally it was done because testing for 0/non-0 is a simple and fast operation. > > So speed it is. Was <1 really slower back then? > Likely not. But the assembly is easier: > jnz <label>; vs cmp EAX 0; jg <label>; That might explain things. > >> Also, boolean logic can be thought of as simple maths, > >> as AND is multiplication and OR is addition. This only makes sense if > >> false == 0. > > false < 1, is what I would expect. > > It even makes it more symmetrical. > How? > Let's start out with a false value f of -1. Now AND it with itself > (f * f), and you get 1, which is true. This does not seem correct. Not knowing anything about assembler(and ignoring history), this seems sensible to me: f is an integral, not a boolean. For it to work like a boolean it needs a cast, implicit or explicit: cast(bool); >0?true:false if(returnsErrorCodes()); implicit cast from return value to bool f*f; integral operation and should return the multiplication f&f; bitwise operation and should return bitwise AND cast(bool) f & cast(bool) f; bitwise operation on booleans ~~ :Pluto |
Copyright © 1999-2021 by the D Language Foundation