Return Values, Exceptions And Special Cases


bzar

A Commando
Joined
Sep 22, 2008
Messages
4,500
Location
Finland
Website
Visit site
(I put this here to pandora development to first get a more limited view from the people Iäve dealt with more here. If this warrants further discussion, this thread should be moved somewhere else)

I'll get straight to the point. I want a new programming language level structure which allows me to deal with special cases in calling functions. Current solutions in most languages are return values or exceptions. In my opinion, return values should be just that, return values. They should be reserved to returning expected information relevant to the program logic eg. what the function should return. Exceptions on the other hand should be reserved to operation-scope exceptional situatuations. This leaves us function-level special cases. Zero divisors. Element not found. Consider this (very) simple example (please, no nitpicking over non-relevat things like syntax):

Code:
float div(float n, float d) {
  return n/d;
}

int main() {
  ...
  z = div(x, y);
  ...
  return 0;
}

Found the potential hazard? Yes, if d=0, the function reaches a special case, where the division cannot be performed. This is something that should be addressed by the caller and checked by the function. Why checked by the function? Because only the function programmer actually knows what it needs. A typical solution is to use the return value to convey success information:

Code:
bool div(float n, float d, float& result) {
  if(d==0) return false;
  result = n/d;
  return true;
}

int main() {
  ...
  if(!div(x, y, z)) {
    (error handling)
  }
  ...
  return 0;
}

How about if the function reach any number of special cases like this? Usually that means defining an enumeration data type to list all the possible special cases and switch-casing between them. This again is error-prone, defines a lot of values only used with one function and still makes return value to signify a special case. This, in my opinion is not good.

How about exceptions? They were made to handle special cases that require some special actions? No. In my opinion exceptions should be used in situations where a whole operation can fail a number of ways. Exceptions should be thrown when the programmer feels they are necessary, not when a library programmer does so. This is because a llibrary programmer can not quantify an operation in the program, and as such cannot generate exceptions relevat to that program logic. The program above could be written with exceptions something like this:

Code:
class ZeroDivisorException: public exception;

float div(float n, float d) {
  if(d==0) throw ZeroDivisorException();
  return n/d;
}

int main() {
  ...
  try {
    z = div(x, y);
  } catch(ZeroDivisionException e) {
    (error handling)
  }
  ...
  return 0;
}

Good news: we now have return values dedicated to return values. Bad news: this is a needlessly heavy framework for something as simple as special cases in functions. Exceptions are a nice concept with some good uses, but it really does not fit here. These kind of special cases should be taken into account no matter what, and they are only relevat right where the function is called. Knowing somewhere down the line that somewhere someone divided by zero is not something anyone can handle in any smart way. The only place where thet can be addressed is right where the function is called. In my opinion, exceptions should always be related to the program's concepts. Consider the following example:

Code:
Book findBookByIBAN(string iban) {
  ...
  Book b = database.query(ibanQuery);
  return b;
}

Now if the database query function throws an exception QueryFailed, the caller may know that some query failed somewhere. This, again, is not something that can be handled in any smart way. This is why it is a good practice to define program-specific exceptions to translate generic errors to something related to the program's concepts.

Code:
class BookNotFoundException: public exception;

Book findBookByIBAN(string iban) {
  ...
  try {
    Book b = database.query(ibanQuery);
  } catch(QueryFailed fail) {
    throw BookNotFoundException();
  }
  return b;
}

See what happened? The database layer gets no actual benefit from using exceptions.

In my opinion, special cases should be handled differently. Both of the approaches above have their merits, but both attempt to make something that does not suit handling special cases into something that does. I propose something like this:

Code:
float div(float n, float d) {
  if(d==0) special ZeroDivisor;
  return n/d;
}

int main() {
  ...
  div(x, y) {
    ZeroDivisor: (error handling)
  }
  ...
  return 0;
}

or

Code:
class BookNotFoundException: public exception;

Book findBookByIBAN(string iban) {
  ...
  Book b = database.query(ibanQuery) {
    QueryFailed: throw BookNotFoundException();
  }
  return b;
}

The custom exception is still thrown, because not finding a book is not something the caller may need to address. It is not a special case, it is an exception.

This could be handled compile-time, because special cases must be handled right after the function call. In my opinion these special cases should be defined as such, that the programmer should address them immediately no matter what. Not making a handler for a defined special case would raise at least a compiler warning. No more gazillions of small exceptions, try-catches, one-function enum return value definitions, and such.

Let me say, after trying to explain this idea to some of my friends over IRC some people asked what this gives over exceptions. I hope I got myself over better here. I really do think there is something to this idea.
 
In my view, an exception should be used whenever a function cannot satisfy its contract due to reasons outside of its control (such as e.g. invocation with parameters not satisfying its precondition or inability to allocate memory).

Code:
  div(x, y) {
    ZeroDivisor: (error handling)
  }
Shouldn't this be:
Code:
  z = div(x, y) {
    ZeroDivisor: (error handling)
  }
If so, how would your syntax look like in larger statements where div(x, y) is called as part of an expression? Would the ZeroDivisor line be per function invocation or per expression?
Your proposal looks like its just an alternate and more concise syntax for exceptions to be honest. Do you mean to inline the error handling code into the div function on a per call basis? If so, then you are basically passing a dictionary with (error condition, error handling code) pairs. Any language with first order functions can do this (<edit>passing the entire local context might be harder though</edit>).

I don't know if you are familiar with the concepts of continuations. You might also be interested in reading chapter 19. Beyond Exception Handling: Conditions and Restarts of the practical common lisp book by Peter Seibel.
 
I wouldn't bother writing code like:

Code:
def div( a, b ):
 if b == 0:
  raise SomeDivideByZeroExceptionIJustMadeUp
 return a / b

I'd just do:

Code:
def div( a, b, ):
 return a / b

Then the calling code, can either do:

Code:
result = div( a, b )

In this code, the calling code believes the values it is passing should always be valid, and would like to have an uncaught exception raised if this is not the case. Or if they want to do something custom when invalid values are passed:

Code:
try:
 result = div( a, b )
except ZeroDivisionError:
 result = 0

Not a full answer or anything, just an observation.

Steve
 
Caine said:
In my view, an exception should be used whenever a function cannot satisfy its contract due to reasons outside of its control (such as e.g. invocation with parameters not satisfying its precondition or inability to allocate memory).
I somewhat agree, but in my opinion special cases in functions are not really exceptions, but alternate program paths that should be addressed no matter what. Not handling a special case is almost like not handling all cases in switches. Also exceptions are needlessly heavy to be used for all error handling.

Caine said:
Code:
  div(x, y) {
    ZeroDivisor: (error handling)
  }
Shouldn't this be
Code:
  z = div(x, y) {
    ZeroDivisor: (error handling)
  }
If so, how would your syntax look like in larger statements where div(x, y) is called as part of an expression? Would the ZeroDivisor line be per function invocation or per expression?
Your proposal looks like its just an alternate and more concise syntax for exceptions to be honest. Do you mean to inline the error handling code into the div function on a per call basis? If so, then you are basically passing a dictionary with (error condition, error handling code) pairs. Any language with first order functions can do this.
Yes, it should. I have to admit, I have not thought much about the actual syntax. Yes, I see how this syntax could work badly in larger expressions. Maybe it could somehow be expression-level? I'm considering the idea more than the actual syntax at this stage.

As to being like exceptions, this system would in low level actually work more like a jump list. The jumps would be determined compile time from the function to caller. In essence a function would have multiple exit points in addition to "return". The program flow would fork if the function could have more than one outcome. Think of the error handling block as a kind of switch-case block.

Caine said:
I don't know if you are familiar with the concepts of continuations. You might also be interested in reading chapter 19. Beyond Exception Handling: Conditions and Restarts of the practical common lisp book by Peter Seibel.
I've already read about concepts, and I'll check the others you linked to, thanks! So far I've not seen any actual solutions to this very common situation that were not either a misuse of some other concept or needlessly complicated.
 
Last edited by a moderator:
Rockthesmurf: If your exception handling is farther down the road you should create new program-specific exceptions. That example was just a trivial example to show the idea. The full benefits of this approach are more visible in larger programs. Also exception handling in no way encourages you to address problems where you should. This can lead to bugs when you don't notice a possible place where an exception can be thrown.
 
Ick... it's all just unnecessary complications. If you know enough to handle exceptions for a special case, you shouldn't have passed that special case in the parameters in the first place.

If a function dies because of invalid preconditions - that's pretty much tough. The pre-conditions should be specified for the function and anyone not using it correctly is going to be making their own problems (However, it should be made law to specifiy pre-conditions in a comment immediately above the function prototype/definition, so that it can pop-up in programming IDE's when you hover over the function name). Hell, the programmer couldn't even be bothered to check he gave you a non-zero number, why the hell do you think the rest of their program is going to work at all? I consider it polite if a function does bother to error out with an error message or an assert() in that case.

I hate, hate, hate exception use to validate parameters. Exceptions are for things that are beyond control of the function when it has been given data within its proper parameters. FileNotFound, PermissionDenied, that sort of thing - the stuff you can't do anything about, can happen at any time, is unexpected and isn't a failure of the program itself (every parameter was correct and a week next Tuesday those same parameters might well just work instead of failing). Passing a function invalid values, however, is a failure of the program itself - it will *only* ever lead to crashes, errors, overwriting memory etc. and putting safety barriers around such things in everyday code is a recipe for disaster. You *will* miss something and the program will still crash.

In your example, specify your function to never take zero as a parameter (so if it is ever called like that, it's a complete programming error), add a small check and an assert to the first line of the function to check parameters are valid (don't bother to clean up - it should *never* happen and if it does, you have no idea what to clean up). Anything above and beyond that is just a waste of CPU time, whether at compile time or not - you should not be verifying parameters programmatically all the time, you can't verify some parameters at all (e.g. is this data that I'm pointing to a valid in-memory structure that's still "alive" or did it get free'd but not zeroed ages ago when it was valid? I.e. is this memory I'm about to use going to be trampled over any second by another pointer that actually "owns" that bit of memory?).

If something is really likely to fail (e.g. open file) then it gets wrapped in every library that uses it with a function that returns success or not, and why not. Special-casing each exception is no more a chore to do than the current situation - i.e. look up error 216 in a table and find it's a FileNotFound error... this already happens quickly and programmatically enough to present error messages to the user. The same as malloc and a million other basic library functions. If you then go on to use the return value regardless, that's your problem. Return values are doing their job here - but again won't be saved if the programmer doesn't bother to check them (how many people check the return value of printf/sprintf? Admittedly most of the time you don't care, but sometimes you *do* assume that sprintf just always succeeds). If you have a function that returns a value and you don't check it - that's your fault if it goes wrong.

You can't babysit everyone - specify a function, the function prototype specifies the type of parameters and that should be "safety" enough to make sure you're not accidentally using the wrong function. The function specification, though, is the final word, usually isn't programmatically expressable, and if you fail to abide by it everything from that point onwards will go wrong. Preventing typos and simple thinkos is easy and already catered for by type-checking and basic assert / return values. Anything else is just making the programmer think "Oh, I'll just throw this crap at it and it will tell me if anything's wrong". It's bad programming.

Other problems are:

- Make me specifically write code to special-case every possible error-instance for a single function that I use ONCE in my entire program to do one thing which, by definition, can never be an incorrect parameter and I will kill you. Or just turn those warnings off immediately.

- 99.9% of those special-case handlers will do nothing more than choke the error back out in some fashion, probably a return value or exception or error message. If something's failed, it's failed... the reason why is interesting only to the programmer and he should already know why it's failed. This is why most error messages go unwritten, untranslated or just plain ignored.

- Once it's compiled to machine code, that new syntax will generate code that's virtually identical to any other method.

- There really is nothing "new" there - you could probably bodge something approaching that syntax with macros if you really wanted to, and you could make the compiler choke on unwritten "special-cases" just the same.

- You don't actually gain anything. The special-case is still present. The code still has to be written to handle it. The function still has to give up and return an error, just in a slightly different fashion. Your function specifications will still be identical.

All you've done is created a slightly different syntax to writing an error handler than the lazy programmers won't want to write anyway(and hence will bodge to make the compiler *think* it's written them). Your code is no more safe, and you still can't "divide by zero" or whatever. It's just syntactic sugar on existing methods, the ones you are complaining about through the rest of your first post.
 
B-ZaR said:
As to being like exceptions, this system would in low level actually work more like a jump list. The jumps would be determined compile time from the function to caller. In essence a function would have multiple exit points in addition to "return". The program flow would fork if the function could have more than one outcome. Think of the error handling block as a kind of switch-case block.
If the jump table could be hard-coded at compile time, i.e. the pairs 'raised error - cought error' would be translated into hard jumps, that would mean that the error checks cannot be placed in a library - they need to sit at the caller's side (as the callee does not know about vectors into the caller). That complicates things a good deal for the compiler.

Conversely, if the jump table would not be hard-coded at compile time, the caller would need to provide all possible 'error handler' vectors for the compiler to put in a dispatch table (i.e. jumps would be idirect), which puts some strain on the cpu decoding logic, the way vtables do. Do you really want the call to a simple arithmetic function to be as costly as a vtable dispatch?

My take on the whole issue is very close to ledow's - assert is your friend. If you suspect something stupid/illegal could happen during development time that would be the caller's fault - by all means - place a corresponding assert at the right place. You can always disable the assert later at the flip of a compiler flag.

If you expected something bad might happen at deployment/normal run-time - provide return codes.

In case of the above where you suspect the caller might not care about the potential error codes returned, but somebody down the line might - throw an exception. Or go ahead and throw an exception regardless, though I personally hate having to write exception traps for things that a simple failure code at return would've sufficed. Another option here is to provide an 'error stack' that gets pushed at an error occurance, and popped at reading - i.e. if foo() raised an error code in the error state, at first getError() of that code is returned to the inquirer, and the stack is popped of one error code, but that would require switches on both the error-raising and error-catching sides.
 
Last edited by a moderator:
B-ZaR said:
Bad news: this is a needlessly heavy framework for something as simple as special cases in functions.

This isn't a very strong argument *on its own*. Its basically 'we can write this nicer' , unfortunately you can only write it (arguably) nicer by added more syntactic complexity to the language. (ie. creating another 'thing' for programmers to get their head around.)


B-ZaR said:
Exceptions are a nice concept with some good uses, but it really does not fit here. These kind of special cases should be taken into account no matter what, and they are only relevat right where the function is called. Knowing somewhere down the line that somewhere someone divided by zero is not something anyone can handle in any smart way. The only place where thet can be addressed is right where the function is called.

You certainly arn't the first person to think like this. Look at Java, they basically ran with your idea, but decided to re-use the exception instead of increasing the syntactic complexity of the language. That is for *some types* of exception the caller is forced to do *something* with them. In fact its pretty much assumed that all exceptions thown in custom-written code will fall into this bracket.

Personally i think java's approach to exceptions is bollocks (or *was* bollocks i havn't done java in years). In practice it just pissed me off, and added useless clutter. Error (or special case) handling is essential, but I have found that paradoxically the *less* of it you do the better too. Squaring that circle can be something of an art, and constructs that you suggest would (like in Java) make matters worse.

I pretty much agree with ledow on this, so i wont repeat it (Although - I think the criticism of div(n,0) was a little unfair - it *was* only an example, there are equivalent (more complex) scenarios which are entirely valid.)
 
Last edited by a moderator:
This is exactly the kind of conversation I wanted. Thanks for everyone so far for commenting.

I may have chosen a bad example by only showing using special cases used with errors. Granted, given the arguments above it may not suit that bill, at least not in all cases. The point of special cases however is larger, and is aimed more toward multiple exit points for a function I mentioned earlier. I'll try to think of a better, yet as simple of an example. Think of cases where no actual error occurs, all of the function's preconditions are met, but the function still cannot return any sensible value.

I'm somewhat against using return codes when the function would have an actual return value (the expected result) to return. In my opinion return values should be reserved to returning the result data of the function call, not metadata about the function exit status.

Okay, better direction: forget about the error handling, think multiple legitimate exit points. :)
 
B-ZaR said:
Think of cases where no actual error occurs, all of the function's preconditions are met, but the function still cannot return any sensible value.

You mean things that work like "NaN" and similar? i.e. the preconditions were okay but there's no determinable "answer" that the function can give? Mathematics has a lot of those sort of things, so you can imagine similar scenarios such as "GraphFunctionNotContinuous", etc. Again, it's still just a fancy type of exception / return code, wrapped in a slightly different syntax. It still ends up getting pushed onto a machine stack somewhere as a special value, no matter what you do, you're just making some nice programming language that hides the fact. And using non-programmatic preconditions (such as "don't give this function a non-continuous graph function") are the only way to sensibly work in that circumstance. Either the function knows that it's going to error out in some way, or it doesn't. If it knows, it shouldn't accept the parameters, if it doesn't it has to have a way to inform the programmer when it happens - how they do that is just syntax. I could literally write a function that may have 20 or 30 "special" answers, all you've done is provide a mechanism to enumerate them and pass them back to the user in a way that doesn't trample over a potentially possible return value (otherwise that would be like returning -1 as a failure code from a function that negates integers).

The fact is that most return values do *not* use the full range of possible values... e.g. it's normally extremely easy to return a -1 or whatever at any point and then have the code check what error occured (e.g. SDL_GetError() would be a good example). If they do use the full range and you can't use *any* particular value to return an error / problem / special case, it's easy to return two or three values even in C by using pointers in the function parameters that get filled with extended return values (e.g. result = function(param1, param2, param3, &error_type); ). However, even FPU's etc. have special bits for "NaN", "Infinity" (usually + and - versions), etc. that cannot be touched by a "normal" answer. *Logistically* speaking all you're doing is creating a "hidden" channel that a function can use to return values by another means - the function is just throwing back your "special" case in a way that doesn't trample over the normal range of return values. That's why I said it could all be done with macros if you really wanted. We're talking a pre-processing stage here - the actual generated code will be logically equivalent.

I know the div by zero example *was* just an example, of course, but it's easy to present the same arguments against any implementation as it is against your example. I don't think you realise that your "multiple exit points" and "special cases" are all just different ways of expressing the same information - you're exiting a function with a particular message for the caller, where you could not satisfy their demands, and having them handle the aftermath.

My main point would be - If the function cannot return any sensible value, then it's a waste of time for the function to be called. Completely. Whether the function checks for those situations, or the caller, is really not important. Your way is not going to help matters, all you've done is moved the checking code for such conditions into the function itself (which makes the programmers lazy and lazy programmers create shit that annoys me all the time) and having it spit them out in an exception-like way. That's no better or worse than the function returning a special value (somehow), throwing an exception and/or just specifying that the *caller* should do those checks first or the results might lead to a crash / nonsense answer.

My analogy would be: You go to the tax office and they tell you to fill in a form so they can calculate your tax. The form has no explanations on it, just 50 numbered boxes. You fill in what you think is right and send it off. They then accept the form, process it, and then send it back to you saying "Sorry, we can't calculate your tax because...". Your method is for them to return it by post using a special set of checkboxes that states all the possible reasons why they couldn't process it ("You didn't fill in section 3.2 even though you had overseas income stated in box 3.1, so we can't calculate your tax"). The programmer then has to do something about that reported problem.

The existing method (return values, exceptions etc.) is for them to do the same but just write a standard letter, or say "your tax form was incomplete" and then - if you want - you can make them go on to explain why. Your method is functionally equivalent to return values / exceptions except in terms of programming convenience, and is liable to errors and comes at a huge programming cost (i.e. you *do* have to account for every single possibility every time. The "tax office" - i.e. the function - can't do anything about things they have no checkbox for, and thus the tax people and yourself will just assume your answer *must* be correct because "there wasn't a box for that", i.e. there wasn't a special case for that).

In programming or in tax offices that's not how it works. They give you sheets and sheets of explanatory material - they are in no way *complete* explanations but they are explanations of roughly what goes into what box. If you stick to their specification (and query it where necessary - because tax explanatory forms are virtually identical to programming specifications in that no bastard ever tells you *exactly* what they mean), you will never file an invalid tax return. However, if you typo and do make a mistake, they can return the answer in either of the above methods and it won't make any difference (i.e. "you indicated that you have a dependent but didn't specify their age, so I don't know if they are an adult or not" - whether that's on a checkbox or a standard letter or even just "you did not correctly fill in section 14.2", it doesn't matter). The return values / exceptions / special cases / etc. are *not* that useful unless you've stuck rigidly to the specification. And if you just throw data at a function without checking the spec, it *will* give you nonsense, even if it has "error-checking" (see my non-zeroed memory example). If a function has a special case that it can't handle, whether it returns a return value, fills in a special "error" container that needs to be checked after each calculation, throws exceptions, sends the user a carrier pigeon etc. it doesn't matter. The problem still has to be checked for (and doing that *in* the function is generally a bad idea because lazy programmers will say that if a function accepts an argument, it *must* return the right answer), reported, and handled.

The *important* part is the specification,and that's where 99.999% of the errors should be caught - anything else is a safety measure against accidental typos or copy/pastes. If you function can't handle certain scenarios and is just going to return nonsense, it should say so in the spec and/or be tested for at runtime and appropriate errors produced. I don't deny that it might be nice sometimes for a function to somehow inform me of a special case that it can't handle, especially when dealing with undocumented or poorly-documented libraries, but the fact is that it shouldn't be the job of any function to check the validity of its arguments in any fashion - it can't do so properly. If it can't return a sensible answer, or doesn't know *when* it might not return a sensible answer, then the function is basically useless.

If you fail to read the instructions in the specification, or choose to ignore them, it's virtually certain that your tax form is complete nonsense, invalid and/or illegal. If you put your income into the expenditure box and vice versa, even though the specification says not to, you'll always end up with nonsense. The specification is the god that you have to appease to be allowed to use the function at all. Anything else is syntactic sugar. In libraries, this is a disaster waiting to happen because people *won't* bother to handle every special case - almost by definition they are using the library because they don't understand the problem or aren't willing/able to write their own code for it. If you make them "handle" every special case in every calling code, you've just made them write the library. If you handle every special case for them, you either have to inform them when they are being stupid (and thus are back to the previous sentence) or carry on and make sense of nonsense.

Please don't make programmers lazier than they are. It's almost impossible to teach a CS student that just throwing values at a library "because it's designed for it" without reading the spec is just a disaster waiting to happen. And if they read the spec, you won't have this problem. And your method is a pre-processor macro, to be honest, that adds an hidden parameter to each function that they can use to return another error code in. I don't deny that it may have value for some people, or that it is probably quite easy to implement, but it doesn't mean that we *should* implement it as a matter of course.

Have a look at any of the big maths libraries out there, doing serious work. Things where the function can get 99% of the way to finding a huge derivative of a given formula (which might take hours) before you can ever realise that it's only going to result in nonsense unless you're working in complex number planes, or 4d surfaces, or whatever. The libraries error out, or throw an exception and then the whole caller tree has to handle that error all the way back. It's the same thing, and those libraries are used by mathematicians, not programmers.

If your function can do something, it should do it. If it "knows" beforehand that it can't do something, it shouldn't even try. If it finds out halfway through that it can't do something - there's nothing you can do but notify the caller (return values / hidden parameters / exceptions / "special cases") and let them get on with it. The same error number / return value ends up on the stack though, one way or another.
 
Last edited by a moderator:
I've many times thought that a nice addition to C would be return to address... I think a "raw version" (show under the hood) would look like:
Code:
int div(return *exception,int a,int B) {
   if (b==0) return DivisionByZero@exception;
   else return (a/ B) ;
}

Calling this would be like:
Code:
int main(void) {
  int rv;
  rv = div(special_case,x,y);
  /* do stuff... */
  return 0;
return_location special_case:
   /* rv is the exception number/value now */
  switch (rv) {
    /* handle special cases */
  }
  return 0;
}

On the assembly level the extra return location parameter would just be pushed on the stack like any parameter, and returning to it would be unwinding the stack + ijmp to the value.

this could be implemented behind the scenes (i dont want to think about the syntax that exactly now...) so that you dont need to add special label for the special case return location and you wouldnt need to pass the location as a parameter, not giving any special case handling code would just make the compiler pass a NULL (that could be checked/asserted if necessary) or the same location as with a normal return (ignoring special case handling (a warning-situation, i guess))....
 
I didn't read all replies as this thread is "too high-level" for me. Personally, I think that a programming language shouldn't abstract more than necessary. Adding an exception handler etc. like this makes it hard to understand the generated machine code (as it could be implemented in tons of ways) and it makes it harder to optimize and debug your code - imho.
If you really want some exception handling like this you should implement it yourself - why not just define some macro to handle the case. I just tried that and got good results with similar syntax (not exactly like that, even though I think that it is possible somehow).

But I agree with ledow: You shouldn't have any problems like this in your code, if something fails it failed and you should fix it.
Unfortunaly this is not always possible, if the problem is in another library you use for example. However, thats a completly different topic.
(glShaderCompiler crashes for me for example under certain circumstances... - thanks nvidia :p [in case anyone reads this: "gl_FragColor = length" in the fragmentshader crashes linux, x64, 195.36.15])

urjaman: Isn't this concept HIGHLY similar to callbacks? Basicly you could just return with a callback like you did and then just set a flag if necessary to ignore further code:

Code:
int exception(int reason) {
  printf("Exception-Handler kicked in\n");
  //Set a global boolean here which can be checked wether more code should be handled differently in another function
  return 0xDEADBEEF;
}

int div(int(*exception)(int),int a,int B) {
   if (exception != NULL) {
    if (b==0) return exception(0);
   }
   return (a/ B) ;
}

int main() {
  printf("Returned %i and %i\n",div(exception,5,1),div(exception,5,0));
  return 0;
}

Using a macro you could also use the exception handler to write an label back into the code where the code will jump to automaticly, but that would need a macro wrapped around the function I think.

Code:
#define div(e,a, B) safeDiv(a, B) ; if (b == 0.0) { goto e; }

float safeDiv(float a, float B) {
 if (b == 0.0) return 0.0;
 return a / b;
}

int main() {
  float a = div(exception,5.0,1.0) { printf("Division a sucessful\n"); };
  float b = div(exception,5.0,0.0) { printf("Division b succesful\n"); };
  printf("Returned %f and %f\n",a, B) ;
  return 0;
  exception:
    printf("Exception-Handler kicked in\n");
    return 0xDEADBEEF;
}

The negative aspect is that it checks twice against 0 to be on the safe side - there might be a better way or hopefuly its optimized out. You could use this code also to check against unsuccessful division like the OP asks for - problem solved?
//Edit: This seems to make the B uppercase automaticly for some strange reason - its lowercase normally..
 
B-ZaR said:
Rockthesmurf: If your exception handling is farther down the road you should create new program-specific exceptions. That example was just a trivial example to show the idea. The full benefits of this approach are more visible in larger programs. Also exception handling in no way encourages you to address problems where you should. This can lead to bugs when you don't notice a possible place where an exception can be thrown.

If the exception is occurring in system, as in your example, then I wouldn't wrap that with my own exception, I'd just handle the system exception.

If the exception is occurring because your function has detected a series of events that can't be handled, then yes you can raise a custom exception if you like.

Steve
 
Last edited by a moderator:
ledow said:
I know the div by zero example *was* just an example, of course, but it's easy to present the same arguments...

It wasn't clear from your first post that you accepted that there was case for handling special cases (or whatever) from within the function. your second post (further down) makes it very clear that you acknowledge these situations. Thats all i was talking about by the 'div(n,0)' crack.
As i said above i agree with you! +1 or whatever the lingo is. (im a forum n00b)
 
Last edited by a moderator:
Yeah, I just prefer exceptions the way they are, but not always their use, like how Java pretty much uses them for everything, ever.

Also a solution for functions validating the arguments (which isn't a bad idea, in case the documentation sucks or something, which I've certainly encountered.) would be to be able to -Dwhatever define them on in development while you're programming, then when you remove the flag, the validation code will just not exist in the final program for releases and not produce slower code. I know this could be error prone (bugs in the code that validates the arguments), but it'd just be one extra thing to, within reason, maybe make things a bit easier in the face of annoying circumstances.
 
Okay... it seems some of you are of a little different school of programming than I am, which makes my point moot for you. I am very much one of those people who like abstract concepts in programming languages. Don't get me wrong, I can deal with C or assembler (to an extent) just fine. I actually do some low level C stuff at work, but I just like things more descriptive. I've never needed to open my compiled code for optimizing. Debugging, yes, but that gets a lot easier with higher level tools. I understand the need for clean machine code in some cases, but so far it has not been a priority for me.

From a purely functional perspective adding special syntactic sugar for "special cases" would only make the language more complex. My point is to add descriptiviness and semantic properties to the code. Different syntax for different meanings. "For" and "while" loops are redundant, but having both adds meaning to the code and makes it easier to understand. When the code tells you more "why" instead of just "what" with a glance, you tend to understand it better and make less mistakes with it. Of course one could always use the generic versions of stuff, but in general, in my opinion, the more descriptive the code is the better. Don't code for the machine, code for the next person reading your code.

Also there was the point of "not dumbing down programming". People make mistakes. Everyone makes mistakes. The more mistakes can be caught by having automatic checks the better. When you forget that one special case of passing function parameters your program may crash at a very unfortunate situation. In my opinion, a few wasted cycles in most cases is not a bad tradeoff for less errors. In addition, by having the parameter check code in one place, written by the one person who knows best what a function should accept, is a lot less error prone than having copy-pasta parameter checking all over your code. I usually, if possible, try to write reactive code instead of proactive. Describing the actual business logic as clearly as possible with as few distractions as possible, and then reacting to possible errors or special cases. Checks that are not related to the thing the program is actually supposed to do only clutter up the code.

Next up, using invalid return values (like -1, for example) to convey status and error messages. As I have stated, I'm against giving multiple meanings to concepts. It makes code more ambiguous and error prone. This is also why I think exceptions are not a valid way to handle special cases. Exceptions should be reserved for operation level stuff for reasons I've stated before.

Well, I've stated my opinion and had many good answers to give me something to think about. There are some obvious differences in how different people from different backgrounds view programming. I taught programming at a university for 2.5 years, so my view is kinda "academic", which is not necessarily always a good thing. I value style over speed, if it doesn't cripple functionality (for example, an emulator working at 100% speed with messy code is better than one working at 50% speed with clean code). I digress. On my part, I'll be happy to agree to disagree here. I've raised the points and made the arguments I had in mind when starting this thread. I might raise this idea again when I've thought about this some more. In my defense, if this sounded stupid, I did not think about this whole idea very much before posting here (as can be seen from the crude syntax). I wanted to raise some conversation to get early opinions, which I did. Again, I thank you all for that. Anyway, because of the programming cultural differences I don't see much more we can accomplish by going over this at this time. I'll of course read any replies to this thread and try to answer any questions, but I'll be more- if you will -reactive than proactive :).
 
Why are you asking in the first place? Do you plan to release a new programming language?
 
Jan-Nik said:
Why are you asking in the first place? Do you plan to release a new programming language?

Just out of curiosity. To raise conversation. Not for any practical purpose in any foreseeable future :p
 
Last edited by a moderator:
My advice: don't use object-oriented exceptions on an embedded device.
Performance and code bloat: For instance, you are not allowed *at all* to ship a commercial C++ XBox360 game with exception handling. And the Xenon is vastly faster than the Pandora (assuming the Pandora does exist :p )
Correctness: You don't know how to make exception-safe C++ code. Even if you believe you can. Maybe Herb Sutter can but neither me nor you :)
Even with a garbage collector, you're not safe, because of the old "GC is for memory reclamation, not object finalization".

That said, if you don't care about these two points, exceptions are nice and convenient.
As I do, I use error codes, "if" and lots of assertion flavours.
 
B-ZaR said:
Next up, using invalid return values (like -1, for example) to convey status and error messages. As I have stated, I'm against giving multiple meanings to concepts. It makes code more ambiguous and error prone. This is also why I think exceptions are not a valid way to handle special cases. Exceptions should be reserved for operation level stuff for reasons I've stated before.

I agree with you from a CS point of view, but ABIs make returning integers in registers the fastest way to convoy a result, be it a legitimate or an error.
 
Last edited by a moderator:
Back
Top