The Right Approach to Exceptions

H. S. Teoh hsteoh at quickfur.ath.cx
Mon Feb 20 13:42:07 PST 2012


On Mon, Feb 20, 2012 at 09:45:50PM +0100, deadalnix wrote:
> I don't think the Condition should be an Exception. I think it
> should provide a method that throw.
> 
> class Condition {
>     void throw() pure {           // Or make it abstract
>         throw new Exception();
>     }
> }
> 
> If no handler is found, this method is called. The handler can also call it.
> 
> It is very important to separate the Condition and the Exception.
> Several Condition may use the same Exception.

Good point. Conflating the two will only lead to bad design and
confusion later on. Best to keep them separate. So Conditions will be an
alternative to Exceptions, and if they are not handled, then revert back
to throwing an Exception like before.


> BTW, I don't think that something that can be implemented as lib
> should be added in the core language.

OK.

Although if we implement it as a lib, then we'll need to "abuse" the
exception throwing mechanism in order to handle error recovery
strategies. For example:

	auto func() {
		try {
			retryOperation1:
			...
			raise(new MyCondition);
			...
			retryOperation2:
			...
			raise(new MyOtherCondition);
			...
		} catch(MyConditionRetry e) {
			goto retryOperation1;
		} catch(MyOtherConditionRetry e) {
			goto retryOperation2;
		}
	}

Alternatively, we can use some mixins that generate goto's, maybe
something like this:

	template BeginRetryBlock(...) {...}
	template EndRetryBlock(...) {...}
	template RecoveryBlock(...) {...}
	template RestartBlock(...) {...}

	auto func() {
		BeginRetryBlock!("retryblock1");
			int some_parameter;
			if (!operation1())
				raise(new MyCondition);

		RecoveryBlock!(MyCondition.Strategy1)
		{
			RestartBlock!("retryblock1");
		}
		RecoveryBlock!(MyCondition.Strategy2)
		{
			fiddleWith(some_parameter);
			RestartBlock!("retryblock1");
		}
		EndRetryBlock!();

		return result;
	}

The templates translate func() into something like this:

	// generated code
	auto func() {
		retryblock1:
			RecoveryStrategy __rs = void;

			int some_parameter;
			if (!operation1()) {
				__rs = __raise(new MyCondition);
				goto __recoveryBlock;
			}
			goto __success;

		__recoveryBlock:
			if (cast(MyCondition.Strategy1) __rs)
			{
				goto retryblock1;
			}

			if (cast(MyCondition.Strategy2) __rs)
			{
				fiddleWith(some_parameter);
				goto retryblock1;
			}

			// No matching strategy, give up
			throw __currentCondition.getException();

		__success:
			return result;
	}

This way, we don't need to use throw until we actually have to.


> Condition may be use using something like raise(new
> MyCondition(params)); with raise from std.condition .
> 
> Alternatively, we could go metaprogramming for Condition, just like we
> did for Range.
[...]

I think metaprogramming is the way to go, otherwise this feature will
require too much typing to achieve something simple, and people won't
like to use it.


T

-- 
We are in class, we are supposed to be learning, we have a teacher... Is
it too much that I expect him to teach me??? -- RL


More information about the Digitalmars-d mailing list