Exceptions...

Started by
60 comments, last by null_pointer 23 years, 10 months ago
In building a major library, does anyone have an idea about how to use exceptions? I''m going to read Bjarne Stroustrop''s (sp?) online Appendix on the subject, but he''s a little wordy for me... Does anyone care to contribute their opinions/experience on the subject? 1) EXCEPTIONS AND LOGIC Anyway, exceptions thrown from a library indicates only (ideally, that is, barring non-functioning hardware, etc.) incorrect logic on the part of the user''s program. In other words, the classes in the library aren''t being used the way they were intended. At this time, I can have the library framework halt the user''s program, cleaning up memory and resources via the destructors, etc. Then it will report the error and give a reference to the documentation on the class. Are there any other ways to use exceptions? Am I being too general? I know they are irritating (like the ASSERT() macros in MFC), but they really do prevent very serious design errors. 2) EXCEPTIONS AND FLOW-CONTROL Obviously, exceptions are not intended to serve as program flow control (the old "open file but it doesn''t exist" theory), as that could easily be done with return values or even properties. But will exceptions occur that can clean up after themselves, and carry on without even being visible to the user''s program? Or is that just some kind of vague science fiction movie stuff? 3) TYPE-CHECKING FOR MONITORING EXCEPTIONS I have an abstract base class that serves as a "catch-all" type, from which I derive all my other exception types to match particular exception types. The question is, should I derive an exception for each class, or should I derive exceptions according to general circumstances? Meaning, Array::Exception or MemoryException. Type-checking is very flexible, and I could certainly do either, but how should I use it in this problem? - null_pointer Sabre Multimedia
Advertisement
quote:Anyway, exceptions thrown from a library indicates only (ideally, that is, barring non-functioning hardware, etc.) incorrect logic on the part of the user''s program.

No. What if you are asking the library to create an object, such as a bitmap surface? If allocating the memory fails, new should throw std::bad_alloc (I believe, correct me if wrong) and the library would either catch it itself, or throw it on down to your application (perhaps based on some switch you set somewhere).

As for the old ''can exceptions clean up and carry on as if nothing has happened'', well of course they can. Just like you can by checking error codes. They aren''t a magic bullet - you need to know -how- to attempt to continue - but they can make it more straightforward to return to a stable state in your program.

The type-checking stuff... don''t overdo it with exceptions. Do you need to be able to throw and catch 100 different types? Part of the philosophy of modularity dictates that you should be able to swap in and out your program sections. If you (or perhaps more importantly, your user) are dependent on this large hierarchy of exceptions for your error handling then you have compromised modularity in the name of inheritance. If you really need more versatility, look into using (or at least inheriting from) the std exception family. There are several in there which are already done for you and are descriptive enough for most needs.

I certainly wouldn''t recommend different exceptions for each class. Do you want users of the library to have to write more different catch blocks than actual code? Of course, they can just catch your generic class, but that doesn''t tell them much - you may as well just throw a const char* for all it''s worth. I''d say limit the number of exceptions and consider passing more information as part of a string inside the exception class.

Anyway, just some personal opinions.
Well, first, I don''t know how you could possibly recover from an exception, so could you please state an example? Exceptions should never be used to control normal program flow. It''s far easier to use return values than exceptions.

Second, I don''t care about std:: because the end user won''t be including anything from the standard libs as it is not an option.

Third, I must use an inheritance tree -- the question is which one I should use. Don''t care about catching 100''s of types of errors, and neither does the user. That''s why I have a catch all in main(). If you don''t catch the exception, the framework quits and notifies how the exception occurred. In other words, when debugging, you insert a catch statement for the type of error you want to catch. Each class would have a generic type as well as specific types. Or, I could simply have several standard types, like OUT_OF_MEMORY or what-not.


- null_pointer
Sabre Multimedia
quote:Original post by null_pointer

Well, first, I don''t know how you could possibly recover from an exception, so could you please state an example? Exceptions should never be used to control normal program flow. It''s far easier to use return values than exceptions.


Why never? That sounds like dogma to me. Exceptions are a tool, if you know what you''re doing, you can use them however you like to achieve your goals. Just as one might need to resort to assembly or the occasional goto. (Although I''ve never found a need for goto, personally.) If using exception handling for handling normal program flow was more difficult than checking return values, then you wouldn''t even have to say to never use it that way, as no-one would. I think you''d find that there are certainly benefits in perverting the exception system for your own needs on certain occasions.

Whether you could possibly recover from an exception depends entirely on when you are going to throw them. If, as it would appear, you would only throw them in dire circumstances, then you are probably right, as far as your project goes.

As a contrived example, though: imagine a program that, at startup, allocated 5mb of memory. It runs as normal, then at some point, it gets an out-of-memory exception. You could catch this, free your 5mb block, and take advantage of the fact that there is 5mb free to send an informative message to the user, save the active documents, and close the program down. Saving data and closing the program are likely to constitute ''normal program operations'' if not ''normal program flow''.

quote:Second, I don''t care about std:: because the end user won''t be including anything from the standard libs as it is not an option.

Not an option? Why is that? Besides, if you''re making a -library- you can use the std exceptions, catch them yourself, and the end user need never know about it. This all depends on your design, of course.

quote:Third, I must use an inheritance tree -- the question is which one I should use.

Again, why? Inheritance isn''t the holy grail.

quote:Don''t care about catching 100''s of types of errors, and neither does the user. That''s why I have a catch all in main(). If you don''t catch the exception, the framework quits and notifies how the exception occurred.


You need to explain further. Are you making a framework or a library, as they are not the same thing, especially not in this context. If you are making a framework, then you can have control of main() and therefore stick a catch(...) in there. You also get to dictate which functions are going to be called in what order, generally. However, if you are making a library, you don''t have control of main(), and your functions may be called from almost anywhere and in any order. Therefore anything your library throws has to be either (a) caught by your library before it reaches user code, or (b) anticipated by the user. For (b) to work, they need to know that they can catch and interpret all exceptions with a minimum of extra code.

quote:In other words, when debugging, you insert a catch statement for the type of error you want to catch.


The whole idea of exceptions is to cut down on error handling code and move it away from normal program flow. This generally means that you might have a function looking like (before exception handling):
void SomeClass::SomeFunction(){    InitialiseSomething();    Var* newVar = new Var();    TestSomething();    DoSomethingOnlySlightlyRelated();    // etc etc etc     return;}

All these things may be calling different types of routines and using lots of different classes. The issue here is, the user doesn''t want to have to put in 1 different catch handler for each line of code just because each line of code can generate 2 or 3 different exceptions. Nor would they want to put exception handling code in all of those functions they are calling as that clutters up the code further. And if they use a generic catch(...) block here, you are implying that they either need to use RTTI, which most don''t want to get into, or don''t really need to know the type of the exception, in which case your exception hierarchy is not useful.

quote:
Each class would have a generic type as well as specific types. Or, I could simply have several standard types, like OUT_OF_MEMORY or what-not.

I''m not sure what the point of per-class exceptions is. If I attempt to allocate some of Class A and some of Class B in a function, I don''t want to have to catch 2 different kinds of ''out of memory'' exceptions.

Personally, when I feel the need to use some sort of ''sophisticated'' exception handling, it tends to be a base Exception class, with 1 or maybe 2 levels of inheritance at the most, producing 6 or 7 distinct exceptions, some sub-system specific.
quote: Original post by Kylotan

Why never? That sounds like dogma to me. Exceptions are a tool, if you know what you're doing, you can use them however you like to achieve your goals. Just as one might need to resort to assembly or the occasional goto. (Although I've never found a need for goto, personally.) If using exception handling for handling normal program flow was more difficult than checking return values, then you wouldn't even have to say to never use it that way, as no-one would. I think you'd find that there are certainly benefits in perverting the exception system for your own needs on certain occasions.


It's not a dogma; just simple reasoning. If you question this, you need to look up what the word exception means. Code that isn't logical simply won't do in the my library. Exceptions should logically replace return values for error-handling, but there are many instances when it is not logical to use exceptions. In other words, sometimes the conditions are so easily predictable or probable that using exceptions after the fact makes no sense at all. Not every branch of program flow must be decided by exceptions. So what is the difference? When should I use them?

For example, there is a file on the most recently used file list in your app, but you cannot be sure whether the file still exists because you certainly can't hook parts of the OS to make sure the user didn't delete the file. Many people say: "Ah hah! This is a job for exceptions! We'll try to open the file first, and if it doesn't exist, then we throw an exception." But that doesn't make sense. It depends on the user not having deleted the file, when in reality it's perfectly fine for the user to delete the file if he doesn't want it. It's his data file; he created it. In other words, it's not an unexpected condition when the user deletes it -- it's a normal, predictable change in the program flow.

In this case, you should check whether the file exists first, then open it. You don't walk through a sliding glass door without checking whether it is open first! There is a limit to what you can do blindly. You SHOULD expect that the program files are all there, and that WOULD be a place to use exceptions. All depends on how probable the error is, and whether or not it the program can logically be expected to handle it.


quote: Original post by Kylotan

As a contrived example, though: imagine a program that, at startup, allocated 5mb of memory. It runs as normal, then at some point, it gets an out-of-memory exception. You could catch this, free your 5mb block, and take advantage of the fact that there is 5mb free to send an informative message to the user, save the active documents, and close the program down. Saving data and closing the program are likely to constitute 'normal program operations' if not 'normal program flow'.


That is a contrived example. However, I'm writing a game library (perhaps it's better termed a VM now). I don't know of any (well-coded) game that will have 5 MB lying around somewhere... I don't know of any program that would have it lying around (unless it's a contrived program, too ), except to prompt the user to close some windows. That's kind of archaic now that most machines use virtual memory though. Even if you are using it on machines that do not use virtual memory, MDI is a very poor (stupid) design. If you want multiple copies, run multiple programs. You should not have to spend time in your code chasing down a mutex and showing the other app's window and then quitting. That's just emulating multiple apps!

Also, I never said anything about 'normal program operations'. Though it would be a neat trick if you could use "exceptions" as any kind of "normal" coding... Perhaps you mean "legal" program operations instead of "normal"? The nature of exceptions is that they are: 1) not ideally occuring and 2) unpredictable. I find no similarities to "normal" in that description.


quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------
Third, I must use an inheritance tree -- the question is which one I should use.
--------------------------------------------------------------------------------

Again, why? Inheritance isn't the holy grail.


It cuts down on the number of exceptions you have to catch, when you want to get and the bottom of something. On a side note: C++ would be next to useless without inheritance. It's not a cure-all but it's mandatory for good design. I'm sure you know about when to use it, though, so I won't go off on a tangent. I realize what you're saying, but my method amounts to little more than deriving nearly empty exception classes. Simple type-checking. Instead of checking the member data to figure out the "type" of the exception, we use type checking. Good code never bypasses something that the language can (obviously) do better.

In other words, I have an abstract global exception object that I use as the base class. In all classes, I derive another exception class, so that I now have File::Exception, String::Exception, etc. The user can get as specific as he wants when catching the objects. He can catch any exception, or just an exception from a particular class type.

Have a LinkedList of File objects? It'd be great to know what class triggered the error and why it occurred. If you went past the end of the list, the LinkedList::Exception would be thrown, instead of some obscure exception from File down the road. All objects can be used improperly, and since the user shouldn't know the inner workings of the class, he shouldn't get a memory access violation when pulling items out of an empty list. The user would know, with LinkedList::Exception, that his logic (most likely a for() loop) was simply incorrect.

Furthermore, each class will have its own specific exceptions (usually only a very few, because my classes are tiny), like File::EndOfFile or something like that, that detail specific causes. Those specific exceptions are derived from the general exception type for that class. So, you can do this:


// catch the end of file exception
catch( File::EndOfFile ) {}

// catch all file exceptions, except of course end of file
catch( File::Exception ) {}



It will be extremely well documented, and indeed it must be to work. I tried to explain before: my exception handling is mostly meant to catch "stupid" errors -- the kind caused by not knowing what you're doing, and not by outside events such as other programs taking up too much memory. I am taking for granted that if the user made a stupid blunder with a class (not opening a file before writing data, etc.), then the program will halt and refer him to a doc on the topic, or at least tell him what went wrong. In any case, with this type of exception, the program cannot continue and must shut down. If my class hierarchy is logical, and the user's code is not, they will NOT work together. So, whenever a logic flaw is encountered, the program must be shut down and the flaw corrected.

Exception catch statements were meant to be placed in key places in the code; not every single function. By the end of debugging, when the program is built for release, there will be only a few exception catch() statements, perhaps in Initialize, Run, and Uninitialize, as well as in some critical portions of the code.

I will be writing my own main, and the only LIBs and headers the user links in will be mine. I have gone over this quite a bit with (very) knowledgeable people, and what I am writing requires that my LIBs are the only ones used.


I believe I see what all the difference of opinion is, though. You are talking about exceptions that solve run-time (external) problems, and I am talking about exceptions that enforce good coding with my library. In other words, design errors. Run-time (external) problems are almost always fatal, and I still don't know of one that can be fixed. So the catch(Exception) in main() will handle cleanup automatically.

All this has made me think quite a lot -- thanks for stimulating my brain! Sometimes it goes to sleep and won't wake up... I think that what I've learned is: "Exceptions are primarily used for normal termination when abnormal conditions are encountered." Basically just good housekeeping, and they help with debugging.



- null_pointer
Sabre Multimedia


Edited by - null_pointer on 4/28/00 5:08:17 PM
quote:Original post by null_pointer

It''s not a dogma; just simple reasoning. If you question this, you need to look up what the word exception means.


Never let a label define purpose

quote: Code that isn''t logical simply won''t do in the my library. Exceptions should logically replace return values for error-handling, but there are many instances when it is not logical to use exceptions. In other words, sometimes the conditions are so easily predictable or probable that using exceptions after the fact makes no sense at all. Not every branch of program flow must be decided by exceptions. So what is the difference? When should I use them?


The point here is that there is -no- absolute line when a return value becomes the worst solution and exception handling becomes the best solution. Your example regarding the file is a good one. Consider these circumstances:

  • The user types in a filename, the program can''t find it since it does not exist.

  • The user clicks on one of the MRU files, and it doesn''t exist.

  • The program generates a scratch file to store temporary data, and later in that function opens it again for reading, except it now doesn''t exist.


The first one is not ''exceptional'' at all, the 2nd one is slightly less common, but the last one is somewhat odd and you might think exception handling might suit you here. The point is, I could come up with another 3 or 4 examples to fit in between the ones above and it would just be a sliding scale. In a limited memory environment, you might think that running out of memory is not exceptional. Yet the std libraries throw std::bad_alloc. There is no right or wrong, only what -you- deem fit for your application. Exceptions are a tool to be used as you choose. Obviously, the closer you stick to some sort of ''standard'' the easier it is for other programmers to use your code. I understand that reason for keeping to the standard paradigm. I just don''t agree with saying "they are called exceptions and therefore only use them when something is exceptional"

quote:All depends on how probable the error is, and whether or not it the program can logically be expected to handle it.

Probability is a sliding scale from 0 to 1. Where do you want your exception handling to start?


quote:That is a contrived example. However, I''m writing a game library (perhaps it''s better termed a VM now). I don''t know of any (well-coded) game that will have 5 MB lying around somewhere...


Hmm, I don''t think it''s that contrived. Sure, 5mb is a bit much, but reserving resources ahead of time in case you need them later is a common and effective plan. Like some systems can only have a limited number of files open at onces, so you open one at the start in case you run out later and need to write something.

quote:That''s kind of archaic now that most machines use virtual memory though.


Hardly. Virtual memory just means it takes longer to hit the limit, not that the limit is removed.

quote:Even if you are using it on machines that do not use virtual memory, MDI is a very poor (stupid) design. If you want multiple copies, run multiple programs.


Well, I wasn''t talking about MDI specifically, but personally I like to be able to see 2 documents side by side without having 2 sets of toolbars, menus, etc. The new Office 2000 apps that use this look ugly and waste a lot of desktop space.

I was just saying that reserving resources ahead of time allows you to shut down more gracefully. You may not care if your Windows app bombs out, but if (for example) I am running a MMORPG and something goes wrong, I want to make a damn good attempt at being able to save the world and the players before it goes down for reboot.

quote:
It cuts down on the number of exceptions you have to catch, when you want to get and the bottom of something. On a side note: C++ would be next to useless without inheritance.


Again I have to disagree, being a big fan of operator and function overloading, references, and templates

quote:I realize what you''re saying, but my method amounts to little more than deriving nearly empty exception classes. Simple type-checking. Instead of checking the member data to figure out the "type" of the exception, we use type checking. Good code never bypasses something that the language can (obviously) do better.


You are requiring the user to know which exceptions they are expecting to catch ahead of time, and write several exception handlers for these. The more derived exception types you have, the more work they have to do, or the more generic an exception they will catch ("I can''t be bothered catching all these, so I''ll just catch SomeClass::exception").

quote:
In other words, I have an abstract global exception object that I use as the base class. In all classes, I derive another exception class, so that I now have File::Exception, String::Exception, etc. The user can get as specific as he wants when catching the objects. He can catch any exception, or just an exception from a particular class type.

Yes, but he needs to know ahead of time what to catch, putting the burden onto him. Why do this:
catch(Exception1){print ("Exception1!")}catch(Exception2){print ("Exception2!")}catch(...){print ("Something generic")} 

when you could do
catch (BaseException ex){print (ex.type + " " + ex.msg)} 

quote:
Have a LinkedList of File objects? It''d be great to know what class triggered the error and why it occurred.


You still can. Instead of
throw File::OutOfMemory 

do:
throw BaseException("File", "OutOfMemory"); 

In essence, the same information is being passed down. The downside is that, yes, someone could accidentally type something wrong and the compiler wouldn''t catch it. The upside is that the user doesn''t have to choose between either (a) ignoring the type of your thrown exception and catching the generic base class, and (b) writing a load of catch blocks for all the different types.

quote:
If you went past the end of the list, the LinkedList::Exception would be thrown, instead of some obscure exception from File down the road. All objects can be used improperly, and since the user shouldn''t know the inner workings of the class, he shouldn''t get a memory access violation when pulling items out of an empty list. The user would know, with LinkedList::Exception, that his logic (most likely a for() loop) was simply incorrect.

I disagree here: if a user doesn''t need to know the inner workings of a class, I''d suggest they don''t need to know a separate exception hierarchy for each class either. File::OutOfMemory, LinkedList::OutOfMemory, SomeObscureClass::OutOfMemory... who cares? We''re out of memory, that''s all we need to know We don''t want to have to write a separate catch handler for those 3 if I use all 3 classes in a function. I''d much rather just catch a generic OutOfMemory exception. Or a generic exception which embeds class and type info within it.

Of course, you could use multiple inheritance here, deriving your OutOfMemory exceptions from a common base. But that starts to get a bit messy too.

quote:
I tried to explain before: my exception handling is mostly meant to catch "stupid" errors -- the kind caused by not knowing what you''re doing, and not by outside events such as other programs taking up too much memory.

Assume that stupid errors are made blindly - and if the user is blind to the likelihood of such an error, they are probably too blind to put all the relevant catch handlers there too.

quote:Exception catch statements were meant to be placed in key places in the code; not every single function.


This supports my point - these key places will need to be able to catch exceptions from X different classes of Y different types, meaning that anyone who wanted to get an accurate idea of what the problem was would have to implement XY catch blocks, no? If you limited it to throwing the same type of exception and embedding the class that threw it in a String member of the exception, you can cut that down to Y catch blocks. If you also embedded the type of error, you could have just 2 catch blocks: 1 for your basic exception class and one (...) for anything totally unexpected. Generally, I find throwing exceptions to be mainly just about passing information "This went wrong here" and therefore it doesn''t really matter how that information gets passed down. But if you have to be able to catch a multitude of types to get the specific info, you''re forcing the user to write a lot of code.

I think how you wish to handle it will depend on what you intend doing with an exception when one gets thrown. In my code, I catch exceptions so I can see silly design errors, as you have said: therefore I just need to know where the error came from. For small projects, simply doing the following is often sufficient!
if (x < 0 && x >= MAX)    throw "X out of bounds in AccessArray"; 

Just stick a const char* handler in there, and bring that up in a messagebox or logfile before quitting the program In this case, having some sort of Array::OutOfBounds would be overkill, harder to catch, and convey no extra information.

I hope you see what I''m getting at (whether it applies to your work or not )
quote:Original post by Kylotan

Never let a label define purpose


Labels exist to define purpose.

Individual programming style is a myth; people just don''t like to admit when they are wrong and so they say "I''ll do it the way it suits me" but they ignore the way it suits the problem. By doing so, they ignore the way the language was intended to be used. You don''t take a language and mold it to your thinking; you must mold your thinking to the language. Human error is far more often to blame than a bad language. The concept of a bad language is that it is unnecessarily far from real life. "Bad language" has nothing to do with "bad programs". Bad programs are ALWAYS the programmer''s fault, for not using the language as it was meant to be used.


quote:Original post by Kylotan

The point here is that there is -no- absolute line when a return value becomes the worst solution and exception handling becomes the best solution.


I''m sorry, but that is an improper abstraction. There IS an absolute line in your examples; you just have too many variables cited to think it exists. But the it certainly does. I am working in an environment with the library where all circumstances for its use are known.

For each complex problem, there are several bad solutions, several good solutions, and ONE and ONLY ONE best solution. You can''t say that the decision is subjective, as we''re talking about source code for computers, not what type of flowers you like.


quote:Original post by Kylotan

quote:
--------------------------------------------------------------------------------
All depends on how probable the error is, and whether or not it the program can logically be expected to handle it.
--------------------------------------------------------------------------------


Probability is a sliding scale from 0 to 1. Where do you want your exception handling to start?


My compound sentence was simply restating itself; the first part should have been dropped because it is obviously the irrational couterpart to the second. Problems occur for logical reasons, and you must solve them logically.


quote:Original post by Kylotan

Hardly. Virtual memory just means it takes longer to hit the limit, not that the limit is removed.


When I need to support a game that takes up more than 2 GB of memory, I''ll look into that... For now, though, if the system doesn''t have enough memory, the game will quit. I''m talking 32 MB minimum, but I have not defined the exact requirements yet.


quote:Original post by Kylotan

quote:
--------------------------------------------------------------------------------

It cuts down on the number of exceptions you have to catch, when you want to get and the bottom of something. On a side note: C++ would be next to useless without inheritance.
--------------------------------------------------------------------------------


Again I have to disagree, being a big fan of operator and function overloading, references, and templates


Why? Being a "fan" of something in coding practice is illogical. Use what models the situation most closely. I never said that inheritance will solve everything. I said it is necessary, just as templates, and operator overloading, and virtual functions, etc.


quote:Original post by Kylotan

quote:
--------------------------------------------------------------------------------

In other words, I have an abstract global exception object that I use as the base class. In all classes, I derive another exception class, so that I now have File::Exception, String::Exception, etc. The user can get as specific as he wants when catching the objects. He can catch any exception, or just an exception from a particular class type.
--------------------------------------------------------------------------------


Yes, but he needs to know ahead of time what to catch, putting the burden onto him.
Why do this:


catch(Exception1){
print ("Exception1!")
}

catch(Exception2){
print ("Exception2!")
}

catch(...){
print ("Something generic")
}


when you could do


catch (BaseException ex){print (ex.type + " " + ex.msg)}




How in the world did you get your comment from my reply!? I told you before, there will be a global catch() in main, for the global derived class type, and it will output a LOT of information about where the exception occurred. The whole purpose of this is to allow the user to depend on ONE catch statement, instead of having to place catch statements everywhere!

In your code example, you are merely re-writing type-checking code. Think about it: you''re adding a variable to determine type, then making the user add the type into the throw statement, etc. Why not throw a File::Exception and be done with it? You are getting the same result. If you want to catch all exceptions, do catch( global_exception_type ) and be done with it. Why catch everything and then determine the type with a string? You''re destroying the type checking of the catch() statements.


quote:Original post by Kylotan

In essence, the same information is being passed down. The downside is that, yes, someone could accidentally type something wrong and the compiler wouldn''t catch it. The upside is that the user doesn''t have to choose between either (a) ignoring the type of your thrown exception and catching the generic base class, and (b) writing a load of catch blocks for all the different types.


Same misunderstanding.


quote:Original post by Kylotan

I disagree here: if a user doesn''t need to know the inner workings of a class, I''d suggest they don''t need to know a separate exception hierarchy for each class either. File::OutOfMemory, LinkedList::OutOfMemory, SomeObscureClass::OutOfMemory... who cares? We''re out of memory, that''s all we need to know We don''t want to have to write a separate catch handler for those 3 if I use all 3 classes in a function. I''d much rather just catch a generic OutOfMemory exception. Or a generic exception which embeds class and type info within it


Correct, but you misunderstood me.

Put class-specific exceptions in classes, and put generic exception types out of classes. Obviously, I wouldn''t duplicate OutOfMemory for every class under the sun, when it''s the same thing all around.


quote:Original post by Kylotan

Assume that stupid errors are made blindly - and if the user is blind to the likelihood of such an error, they are probably too blind to put all the relevant catch handlers there too.


Same misunderstanding. Exceptions force themselves to be reported by nature, and I will add a LOT of information to their reporting. The user won''t get an "unhandled exception error" but instead reasonable output from a catch( base_exception_type ) in main().


quote:Original post by Kylotan

This supports my point - these key places will need to be able to catch exceptions from X different classes of Y different types, meaning that anyone who wanted to get an accurate idea of what the problem was would have to implement XY catch blocks, no? ...


No. Same misunderstanding. Use the generic exception type for that class or the global exception type.


quote:Original post by Kylotan

I think how you wish to handle it will depend on what you intend doing with an exception when one gets thrown. In my code, I catch exceptions so I can see silly design errors, as you have said: therefore I just need to know where the error came from. For small projects, simply doing the following is often sufficient!


if (x < 0 && x >= MAX)
throw "X out of bounds in AccessArray";


It isn''t going to be used for small projects, and anyone that uses this library must know C++ well beforehand. If you don''t want information in the exception class, don''t use it. I though I explained before that the library is more of a VM, taking over the OS, the hardware, available memory, etc. Duplicating the hundreds of errors for each type of hardware and software it will work with would mean putting 1000''s of error values and USELESS logic inside the user''s program. I''m making a VM (or perhaps a Virtual OS?) here, not a wrapper for everything under the sun. So, if there is an out-of-memory error, I handle it. I have a certain responsibility to make sure that a certain level of hardware is present before I turn the control to the game. I can''t make the user add in 20 switch statements and hundreds of program flow paths for each configuration.



- null_pointer
Sabre Multimedia
Ok, I''m not gonna bother quoting, as it would be way too long

Firstly, saying a label is there to define purpose is just plain wrong in my opinion. I''d say it''s there to describe purpose, not define it. When you start limiting your thoughts about a concept because of the name, you''re thinking ''inside the box'' which is often hindering an ability to realise potentially better solutions.

I would also disagree with the ''mold thinking to suit the language'' idea too. Humanity has evolved through being able to advance thought and language side by side, using one to augment the other. I don''t see why computer programming should be any different.

Concession: since you are working on a library where you know all the circumstances of its use, I will agree that you could be a little more strict with your ideas of how to work it all. I guess I am speaking more about the general picture.

I''ll also disagree with the "there is always only one best solution" assertion. What about a performance/modularity trade-off? Perhaps in -your- design spec, you know exactly what to go for. But someone making a game may not know until it''s been published whether they''re ever gonna need to extend or re-use it.

"Being a "fan" of something in coding practice is illogical." Hardly. My point here was not that there may be preferred methods in C++ of doing the same thing, but that other languages lack these features, and therefore C++ appeals to me. Sure, I can use standard functions in C which are the closest match to the problem, but since I ''like'' overloading, mainly because as you say, it models the situation more closely, I prefer C++. And this particular paragraph was about whether C++ would be useless without inheritance, not about using different tools to solve the same problem

Onto your global catch block in main... I''m assuming that it would call virtual functions in the base exception to query the class about what it is and where it came from?

I think my misunderstanding is perhaps in the difference between an exception hierarchy you choose to use within your own framework, and the exception hierarchy you will end up throwing down to user code. I think that if you ensured that the user will only have to deal with a subset of your exceptions, it would be fine. When Programmer 1 does all the ''throw''s and Programmer 2 does all the ''catch''es, then the more different exceptions you use, the more unwieldy it gets. But within your own system, you can structure your throws/catches however you like as you always know the order of function calls, etc.

Aren''t you glad you and I don''t have to work on the same project?

All the best.
OK, we'll stop the quote wars!

Even if you are correct about labels merely describing purpose, how can you say to use exceptions in a way that is exactly opposite to the description? You are using a language feature to do something it was never designed to do. The outcome must logically be seriously crippled, no matter how you try compensate for it.

As for using C functions as they are the closest model to the problem, they most certainly are not. Find a function not belonging to an object in real life, and I will concede that point.

Typical human thinking is VERY illogical; it is never ideal. It must be shaped and molded to the correct form. There is no perfect "person", but there is only one correct form of logic. Logic is subject to definite rules and laws. Presuppositions are individual, logic is not. Never substitute opinions for fact, and you'll prevent most coding errors straight off.

Back to the point though, typical programmer thinking is about 100x worse than typical "language lawyers" (the people who actually create or know the entire language by heart, including its purpose). So I would say that the languages are typically more logical than typical human thinking. In order to grow, you mold your thinking to that which is more logical. Regardless, any time you use a language feature as it was not intended (even in the name of correcting an illogical design in the language), you cannot logically be providing the best solution.

There is ALWAYS one best way to solve the problem at hand. Programming is not about preferences. Preferences, on the other hand, tell you that one way is always the best way to solve the problem. You limit your coding everytime you make preferences. If you happen to like something and it suits the problem, that is fine. But you should use it because it works, and because it is fast and clean...not because you prefer anything. There are times when inheritance is a better model, and times when templates are better. The best programmer does not limit himself to a subset of the language but instead uses all parts of the language according to the problem at hand. Perhaps I should list what I consider to be the perfect coding style? Good code is:


    1) a logically accurate model of the interaction between any problem and the human brain
    2) faster than "bad" code
    3) cleaner than "bad" code
    4) more easily read than "bad" code



If you rate the possible solutions according to those criteria, then there can logically be only one best solution to every problem. We should make a difference here between typical human logic and ideal human logic. We rate good code according to ideal human logic (because it is what people should be striving for), so we then use a standard based on ideal human logic to test whether a piece of code is good or bad. If you use typical human logic (individual human logic) to test whether a piece of code is good or bad, you gain nothing in terms of programming experience -- you would have as much chance of believing facts as believing fiction.

You say that there is no "line" that works for every problem, but can you fit a bad solution to the above criteria? Can you fit anything but one solution to the above criteria? Problems don't exist without having their own unique circumstances. Those unique circumstances determine what's the best solution for that problem. In other words, the solution is encoded within the problem.


Concerning the global catch block in main(), it will use RTTI to determine the type and output it, along with a short description. Probably virtual output functions or a data member will provide the description. Or perhaps the type is not needed, as the description would suffice? Whichever is logically correct!

Actually, I'd like to work on the same project some time. We definitely make each other think!

Or do we just like to disagree? LOL

Anyway, thanks for replying so much



- null_pointer
Sabre Multimedia


Edited by - null_pointer on 4/30/00 6:38:24 PM
I have read all and must speak!!!

quote:Original post by null_pointer
Even if you are correct about labels merely describing purpose, how can you say to use exceptions in a way that is


Actually I disagree with both of you

Labels are just that, nothing more.

Explain to me how "computer" describes both the physical object and what it can do. Wait a minute, describe it without labels. They are an end run into the human need to externalize the visible world. Otherwise, we could all run around (if we had esp) and the "concept" of a computer would never leave our lips. We each conceive of the computer in a different way (Myself I see a Quantum computer pumping out 100x a man''s own brain, so I can play Whole-Life in my VR gear, with real shock forces when I get hurt... I digress)....

quote:
the problem, they most certainly are not. Find a function not belonging to an object in real life, and I will concede


Fair enough. I point you in the realm of the clouds. Clouds move, one would say that is a function, but, the clouds don''t move by themselves. They are pushed by the wind, which is a function of the thermal emissions of the planet, which are from the gravametric and magnetic forces, etc.. etc...

IOW, clouds are objects and have the appearnt function of mobility, yet don''t have this function. You seem to have skipped over Newton''s law here that for every action there is an equal and oppisite reaction.

Just because an object can be made to function in a manner it was not intended, does not attach the function to the object.

I think you are getting objects and meta objects confused there.

Honestly, is it a function of a flashlight to generate light? or is it the function of a light switch to connect the circut which enables the battery to fire some protons from one end to the other, thus generating a force, which in turn excites the cathode in the bulb to emit some photons??

You see, by your generalization, you have encapsulted the world, the universe even, into one huge function.

Universe(google energy, google mass) {};~Universe() { sprintf("Damnit!@"); } 


quote:
Typical human thinking is VERY illogical; it is never ideal. It must be shaped and molded to the correct form. There is no perfect "person", but there is only one correct form of logic. Logic is subject to definite rules and laws.


without the philosophacil debate, I point you to Aristotle and Plato. They actual work frm something called the ether, from which GOD has formed one "perfect" thing for each sub-perfect thing in the material plane to inherit from. Needless to say, it is a compelling theory of existance.

Just thought I''d add my 0.02

Also, don''t think that logic is an end that justifies the means. If that were the case, people would never pusht he envelope on higher thought.

Case in point, the human brain. It is only 50% logical, from a quantitative view. The other half is utter chaos. Course in my case that might be 5/95 ... %)





This topic is closed to new replies.

Advertisement