The Right Approach to Exceptions

Juan Manuel Cabo juanmanuel.cabo at gmail.com
Mon Feb 20 17:01:03 PST 2012


>
> Jose's argument convinced me otherwise. I retract my agreement.
>
...
>
> No, I'm afraid there's a sizeable misunderstanding here.
>
>
> Andrei

Hahah, yeah, I think there is a sizeable misunderstanding: unless you
are referring to another guy with a spanish name in this thread,
(which I haven't found). My name is Juan Manuel (people call me:
Juanma, JM or Juan, but José is a first! I wouldn't mind John
which is the 'translation' of my name).

Back to the nasty argument. I think that the example that everyone
wants is this one. If anyone solves this one without Variant[string]
then it's a better solution than Variant[string]. (I repaste it
from an above reply I gave):

  [..]
  For instance: a C library wrapper, which gets the library errors encoded
  as some error code and throws them as exceptions. Shouldn't the library
  throw a FileNotFoundException when that's the error, instead of throwing
  a LibraryException that has the error code in a field?

  So the correct thing to do is: after a library call, the wrapper
  checks the last error code number with a switch statement, and deciding
  which standard exception type to throw (defaulting to whatever you like
  if the error code doesn't map to a standard D exception). Then you
  add the error code to the Variant[string], and any other extra info.

  That way, exception types can be standard.

  So, to keep D exception types standard reusable and respected by
  future code, you must follow the Open-Closed design principle
  (nicest principle of OO design ever).
  [..]

Adding the Variant[string] is considered applying the great
Open-Closed Design Principle:
	-Open for reuse.
	-Closed for modification.
        http://www.objectmentor.com/resources/articles/ocp.pdf

--jm


On 02/20/2012 09:38 PM, Andrei Alexandrescu wrote:
> On 2/20/12 6:25 PM, H. S. Teoh wrote:
>> On Mon, Feb 20, 2012 at 05:15:17PM -0600, Andrei Alexandrescu wrote:
>> Formatting should use class reflection. We already discussed that, and
>> we already agreed that was the superior approach.
> 
> Jose's argument convinced me otherwise. I retract my agreement.
> 
>> When you're catching a specific exception, you're catching it with the
>> view that it will contain precisely information X, Y, Z that you need to
>> recover from the problem. If you don't need to catch something, then
>> don't put the catch block there.
> 
> That's extremely rare in my experience, and only present in toy examples that contain a ton of "..." magic.
> 
>> The problem with using Variant[string] is that everything gets lumped
>> into one Exception object, and there's no way to only catch the
>> Exception that happens to have variables "p", "q", and "r" set in the
>> Variant[string].
> 
> No. You are still free to define as many exception classes as you deem necessary. Please let's not pit the hash against
> the hierarchy again. This is confusing the role of the two. Consider the hash an interface function you want to
> occasionally implement.
> 
>> You have to catch an exception type that includes all
>> sorts of combinations of data in Variant[string], then manually do tests
>> to single out the exception you want, and rethrow the rest. That's where
>> the ugliness comes from.
> 
> Yah, that would suck, but it's not at all what I say.
> 
>> [...]
>>> The code with Variant[string] does not need combinatorial testing if
>>> it wants to do a uniform action (such as formatting). It handles
>>> formatting uniformly, and if it wants to look for one particular field
>>> it inserts a test.
>>
>> Again, we've already agreed class reflection is the proper solution to
>> this one.
> 
> Agreement rescinded. Sorry! Jose's point was just too good, and reminded me of a pain point I so long had with
> exception, I'd got used to it as a fact of life.
> 
>>>> And then what do you do if you're depending on a particular field to
>>>> be set, but it's not? Rethrow the exception? Then you have the stack
>>>> trace reset problem.
>>>
>>> Don't forget that Variant[string] does not preclude distinct
>>> exception types. It's not one or the other.
>> [...]
>>
>> Agreed. But it shouldn't be the be-all and end-all of data passed in
>> exceptions. If anything, it should only be rarely used, with most
>> exception classes using static fields to convey relevant information.
> 
> And to perfectly help code duplication everywhere.
> 
>> I can see the usefulness of using Variant[string] as a way of
>> "decorating" exceptions with "extra attributes", but it shouldn't be the
>> primary way of conveying information from the throw site to the catch
>> site.
>>
>> As for iterating over the information in the most derived class, for
>> formatting, etc., class reflection is the way to go.
> 
> Agreement rescinded as far as exceptions go. That doesn't make reflection any less necessary btw. It just reflects the
> dynamism of exception paths.
> 
>> We shouldn't be
>> using Variant[string] for this, because there's another problem
>> associated with it: suppose MyException sometimes gets "extra_info1" and
>> "extra_info2" tacked onto it, on its way up the call stack, and
>> sometimes not. Now what should the catcher do?
> 
> Use a central loop to render the information.
> 
>> How do you format this
>> exception?
> 
> With a string template as has been discussed.
> 
>> Should the format string include extra_info1 and extra_info2,
>> or not? If it doesn't, what's the use of this extra info? If it does,
>> what happens if these fields are missing?
> 
> Decision belongs to the string template engine.
> 
>> This is what I mean by not being able to depend on whether some data is
>> there. Ultimately, to do anything useful with the info in the object,
>> you need to know what's there.
> 
> No. You do realize you are positioning yourself straight against every OO principle there is? The entire OO world is
> predicated on the assumption you _can_ do useful stuff with an object WITHOUT knowing what's there.
> 
>> Preferably, the object's type will tell
>> you exactly what's there, then you do a simple map from type to list of
>> available attributes (e.g., map exception type to format string with
>> known, static list of attributes). But if the type doesn't guarantee
>> what data will be present, then your code becomes vastly more complex,
>> you have to deal with potentially all possible combinations of what's
>> there and what isn't.
> 
> I think you are misunderstanding the mechanisms involved. There is no combinatorial code, just simple iteration and
> such. Dealing with distinct exceptions with distinct code IS combinatorial, repetitive, and non-scalable.
> 
>> Instead of a single format string for a single
>> exception type, you now have a combinatorial explosion of format strings
>> for every possible combination of missing/present fields in the
>> exception object.
> 
> No, I'm afraid there's a sizeable misunderstanding here.
> 
> 
> Andrei



More information about the Digitalmars-d mailing list