The Right Approach to Exceptions

Juan Manuel Cabo juanmanuel.cabo at gmail.com
Mon Feb 20 13:19:35 PST 2012


I forgot to add, that you could define standard details names
as string constants, and even document them in the string constant
definition.

--jm


On 02/20/2012 06:11 PM, Juan Manuel Cabo wrote:
> Yeah.. that is a problem! :-) Thanks for liking the idea, now we can
> talk about the fine details!!
> 
> One way is to not let the user direct access to the associative array,
> but wrap the e.info["MyDetail"] call in a nothrow function, such as
> e.info("MyDetail"), and an e.hasInfo("MyDetail"), and of course:
> e.addInfo("MyDetail", value) and e.allInfoNames() or something.
> 
> The nothrow function would return an empty value if not found (I fear
> that it might not be of the same Variant subtype as the Variant value
> was intended when present).
> 
> --jm
> 
> 
> On 02/20/2012 05:53 PM, H. S. Teoh wrote:
>> On Mon, Feb 20, 2012 at 05:31:28PM -0300, Juan Manuel Cabo wrote:
>>>> ...
>>>> Sure. Again, this is not advocating replacement of exception hierarchies with tables!
>>>> ... 
>>>>
>>>> Andrei
>>>>
>>>
>>> I think that the case of rethrowing an exception with added detail is
>>> the worst enemy of clean Exception hierarchies.
>>
>> Hmm. This is a valid point. Sometimes you want to add contextual details
>> to an exception in order to provide the final catching code with more
>> useful information. Otherwise you may end up with a chain of mostly
>> redundant exception classes:
>>
>> 	class UTFError : Exception {...}
>> 	class LexUTFError : LexUTFError {
>> 		int line, col;
>> 		...
>> 	}
>> 	class ConfigFileParseError : LexUTFError {
>> 		string cfgfile_name;
>> 	}
>>
>> 	auto decodeUTF(...) {
>> 		...
>> 		throw new UTFError;
>> 	}
>>
>> 	auto configLexer(...) {
>> 		try {
>> 			...
>> 			decodeUTF(...);
>> 		} catch(UTFError e) {
>> 			throw new LexUTFError(...);
>> 		}
>> 	}
>>
>> 	auto configParser(...) {
>> 		try {
>> 			...
>> 			configLexer(...);
>> 		} catch(LexUTFError e) {
>> 			throw new ConfigFileParseError(...);
>> 		}
>> 	}
>>
>>
>>> The idea of Variant[string] remedies that case without creating a new
>>> exception class just for the added fields. If that case is solved,
>>> then the tipical need for creating new exception types that don't
>>> really aid selecting them for catching and recovery is solved too.
>> [...]
>>
>> However, I still hesitate about using Variant[string]. How would you
>> address the following problem:
>>
>> 	// Module A
>> 	class MyException : Exception {
>> 		this() {
>> 			info["mydetail"] = ...;
>> 		}
>> 	}
>>
>> 	// Module B
>> 	auto func() {
>> 		try {
>> 			...
>> 		} catch(MyException e) {
>> 			if (e.info["mydetail"] == ...) {
>> 				...
>> 			}
>> 		}
>> 	}
>>
>> If module A's maintainer renames "mydetail" to "detail", then module B
>> will still compile with no problem, but now e.info["mydetail"] doesn't
>> exist and will cause a runtime error at worst. At best, the catch block
>> won't be able to recover from the error as it did before, because now it
>> can't find the info it was looking for.
>>
>> If "mydetail" had been a field stored in MyException, then module B
>> would get a compile-time error, and the problem can be fixed
>> immediately, instead of going unnoticed until it blows up at the
>> customer's production server.
>>
>>
>> T
>>
> 



More information about the Digitalmars-d mailing list