April 02, 2013
02-Apr-2013 17:43, Andrei Alexandrescu пишет:
> On 4/2/13 7:59 AM, Dmitry Olshansky wrote:
>> 02-Apr-2013 15:35, Andrei Alexandrescu пишет:
>>> On 4/2/13 7:24 AM, Dmitry Olshansky wrote:
>>>> You might want to add Visitor pattern to Exceptions but it's darn messy
>>>> to deal with and is an overkill most of the time.
>>>
>>> Actually I think that's a good thing to do.
>>>
>>
>> Why would be that? It doesn't solve the key problem of "try clause plus
>> a ton of semi-identical catches" used just to perform a mapping of X
>> handlers to Y subsets of errors. Plus visitor does the same dispatch
>> that is already addressed by exception handlers (or partly so).
>
> Visitor allows centralized and flexible handling of exceptions.
>
>> If somebody comes up with a reasonable Visitor pattern for Exceptions
>> that is flexible and fast then sure let's see it. I just doubt it'll
>> help anything on its own in any case.
>
> Well I think exceptions + factory + visitor is quite the connection.

The only place where visitor would fit nicely that I can think of is walking an exception chain.

Other then this for the moment let me stick with
"talk is cheap, show me the code" position :)


-- 
Dmitry Olshansky
April 02, 2013
On Tue, 02 Apr 2013 12:34:25 -0400, Jacob Carlborg <doob@me.com> wrote:

> On 2013-04-02 15:44, Steven Schveighoffer wrote:
>
>> Yes, this could help.  But it's still not great.  One must still store a
>> "type identifier" in the ex, or have to deal with casting to figure out
>> what type ex is.
>
> You would have the same problem if you used a common function for handling multiple exceptions.

Right, but what I see here is that the language uses one set of criteria to determine whether it should catch, but it's difficult to use that same criteria in order to process the exception.  It's not easy to switch on a class type, in fact it's downright ugly (maybe we need to come up with a way to do that in normal code too).

>> It also promotes creating a new type for every single catchable situation.
>>
>> Consider that we could create one exception type that contains an
>> 'errno' member, and if we have the ability to run extra checks for
>> catching you could do:
>>
>> catch(ErrnoException ex) if (ex.errno == EBADF || ex.errno == EBADPARAM)
>> {
>>     ...
>> }
>
> Is that so much better than:
>
> catch (ErrnoException ex)
> {
>      if (ex.errno == EBADF || ex.errno == EBADPARAM)
>      { /*handle exception */ }
>      else
>          throw ex;
> }

Yes.  I won't forget to re-throw the exception.  Plus, it seems that you are saying "catch this, but if it's also that, then *really* catch it".  I think the catch is a one-shot deal, and should be the final disposition of the exception, you should rarely have to re-throw.  Re-throwing has it's own problems too, consider this possibility:

catch(ErrnoException ex) if(ex.errno == EBADF || ex.errno == EBADPARAM)
{
   // handle these specifically
}
catch(Exception ex)
{
   // handle all other exceptions
}

I think you would have to have nested try/catch statements to do that without something like this.

>> Either way,
>> every time you catch another errno exception, we are talking about
>> instantiating another type.
>
> Does that matter? It still need to create a new instance for every exception thrown. Or are you planning on changing the "errno" field and rethrow the exception?
>

I mean it's a waste of code space and template bloat.  Not a waste to create the exception.

-Steve
April 02, 2013
On Tuesday, April 02, 2013 17:56:07 Lars T. Kyllingstad wrote:
> On Monday, 1 April 2013 at 22:26:39 UTC, Jonathan M Davis wrote:
> > In general, I'd strongly suggest having subclasses for
> > the various "Kind"s in addition to the kind field. That way,
> > you have the
> > specific exception types if you want to have separate catch
> > blocks for different
> > error types, and you have the kind field if you just want to
> > catch the base
> > exception.
> 
> Then you'd have two points of maintenance when you wish to add or remove an error category.

True, but it's also trivial to do. But if we had to decide between basically putting error codes on exceptions and using sub-classes, I'd vote for subclasses in most cases - though errno would need to go the error code in the exception route, since it has different meanings in different contexts and would risk an absolute explosion of exception types anyway; though it should probably be translated to a more meaningful exception based on context with the errno exception being chained to it - which is what you suggest in the DIP IIRC. In general though, I'd favor subclasses, and I don't think that it's all that big a deal to give them each specific error codes when you want the base class to have an error code like you're suggesting.

- Jonathan M Davis
April 02, 2013
On 4/2/2013 3:40 AM, deadalnix wrote:
> On Monday, 1 April 2013 at 20:58:00 UTC, Walter Bright wrote:
>> On 4/1/2013 4:08 AM, Lars T. Kyllingstad wrote:
>> 5. Although a bad practice, destructors in the unwinding process can also
>> allocate memory, causing double-fault issues.
> Why is double fault such a big issue ?

In C++, such aborts the program as the runtime can't handle it. In general, though, it's a hard to reason about problem.


>> 6. Memory allocation happens a lot. This means that very few function
>> hierarchies could be marked 'nothrow'. This throws a lot of valuable
>> optimizations under the bus.
> Can we have an overview of the optimization that are thrown under the bus and
> how much gain you have from them is general ? Actual data are always better when
> discussing optimization.

For Win32 in particular, getting rid of EH frames results in a significant shortening of the code generated (try it and see). In general, a finally block defeats lots of flow analysis optimizations, and defeats enregistering of variables.


>> 7. With the multiple gigs of memory available these days, if your program runs
>> out of memory, it's a good sign there is something seriously wrong with it
>> (such as a persistent memory leak).
>>
>
> DMD regularly does.

I know DMD does, and I regard that as a problem with DMD - one that cannot be solved by catching out-of-memory exceptions.
April 02, 2013
On Tue, Apr 02, 2013 at 01:29:00PM -0400, Jonathan M Davis wrote:
> On Tuesday, April 02, 2013 17:56:07 Lars T. Kyllingstad wrote:
> > On Monday, 1 April 2013 at 22:26:39 UTC, Jonathan M Davis wrote:
> > > In general, I'd strongly suggest having subclasses for the various "Kind"s in addition to the kind field. That way, you have the specific exception types if you want to have separate catch blocks for different error types, and you have the kind field if you just want to catch the base exception.
> > 
> > Then you'd have two points of maintenance when you wish to add or remove an error category.
> 
> True, but it's also trivial to do. But if we had to decide between basically putting error codes on exceptions and using sub-classes, I'd vote for subclasses in most cases - though errno would need to go the error code in the exception route, since it has different meanings in different contexts and would risk an absolute explosion of exception types anyway; though it should probably be translated to a more meaningful exception based on context with the errno exception being chained to it - which is what you suggest in the DIP IIRC. In general though, I'd favor subclasses, and I don't think that it's all that big a deal to give them each specific error codes when you want the base class to have an error code like you're suggesting.
[...]

IMO, errno should be stored as-is in a dedicated ErrnoException. Any interpretation of errno thereof should wrap this ErrnoException inside another hierarchy-appropriate exception. For example:

	void lowLevelIORoutine(...) {
		if (osRead(...) < 0) {
			throw ErrnoException(errno);
		}
		...
	}

	void libraryRoutine(...) {
		try {
			lowLevelIORoutine(...);
		} catch(ErrnoException e) {
			if (e.errno == ENOENT) {
				// Chain ErrnoException to FileNotFoundException
				throw new FileNotFoundException(e.msg, e);
			} else if (e.errno == ENOSPC) {
				// Chain ErrnoException to DiskFullException
				throw new DiskFullException(e.msg, e);
			} else {
				// etc.
				...
			}
		}
	}

This way, user code can catch IOException rather than ErrnoException, but errno is still accessible via .next should the user code want to deal directly with errno:

	void userCode() {
		try {
			auto f = File("/some/path/to/file");
		} catch(IOException e) {
			if ((auto f = cast(ErrnoException) e.next) !is null)
			{
				handleErrno(f.errno);
			}
			...
		}

I think this would be a good use of the current .next field in Exception.


T

-- 
A mathematician is a device for turning coffee into theorems. -- P. Erdos
April 02, 2013
On 4/2/2013 4:42 AM, Don wrote:
> I think that view is reasonable, but then I don't understand the reason to have
> Error in the first place! Why not just call some kind of abort() function, and
> provide the ability to hook into it?

It would seem to be a fair amount of bookkeeping code to make a hook that would work with many different pieces of code dealing with Errors in different ways.

April 02, 2013
On Tuesday, April 02, 2013 10:41:04 H. S. Teoh wrote:
> On Tue, Apr 02, 2013 at 01:29:00PM -0400, Jonathan M Davis wrote:
> > On Tuesday, April 02, 2013 17:56:07 Lars T. Kyllingstad wrote:
> > > On Monday, 1 April 2013 at 22:26:39 UTC, Jonathan M Davis wrote:
> > > > In general, I'd strongly suggest having subclasses for the various "Kind"s in addition to the kind field. That way, you have the specific exception types if you want to have separate catch blocks for different error types, and you have the kind field if you just want to catch the base exception.
> > > 
> > > Then you'd have two points of maintenance when you wish to add or remove an error category.
> > 
> > True, but it's also trivial to do. But if we had to decide between basically putting error codes on exceptions and using sub-classes, I'd vote for subclasses in most cases - though errno would need to go the error code in the exception route, since it has different meanings in different contexts and would risk an absolute explosion of exception types anyway; though it should probably be translated to a more meaningful exception based on context with the errno exception being chained to it - which is what you suggest in the DIP IIRC. In general though, I'd favor subclasses, and I don't think that it's all that big a deal to give them each specific error codes when you want the base class to have an error code like you're suggesting.
> 
> [...]
> 
> IMO, errno should be stored as-is in a dedicated ErrnoException. Any interpretation of errno thereof should wrap this ErrnoException inside another hierarchy-appropriate exception. For example:
> 
> void lowLevelIORoutine(...) {
> if (osRead(...) < 0) {
> throw ErrnoException(errno);
> }
> ...
> }
> 
> void libraryRoutine(...) {
> try {
> lowLevelIORoutine(...);
> } catch(ErrnoException e) {
> if (e.errno == ENOENT) {
> // Chain ErrnoException to FileNotFoundException
> throw new FileNotFoundException(e.msg, e);
> } else if (e.errno == ENOSPC) {
> // Chain ErrnoException to DiskFullException
> throw new DiskFullException(e.msg, e);
> } else {
> // etc.
> ...
> }
> }
> }
> 
> This way, user code can catch IOException rather than ErrnoException, but errno is still accessible via .next should the user code want to deal directly with errno:
> 
> void userCode() {
> try {
> auto f = File("/some/path/to/file");
> } catch(IOException e) {
> if ((auto f = cast(ErrnoException) e.next) !is null)
> {
> handleErrno(f.errno);
> }
> ...
> }
> 
> I think this would be a good use of the current .next field in Exception.

Yes. That seems like a good approach and is essentially what I meant.

- Jonathan M Davis
April 02, 2013
On 4/2/2013 4:59 AM, Dmitry Olshansky wrote:
> If somebody comes up with a reasonable Visitor pattern for Exceptions that is
> flexible and fast then sure let's see it.

It doesn't really need to be fast. If you need performance out of Exceptions, you're misusing the idiom.

April 02, 2013
02-Apr-2013 21:48, Walter Bright пишет:
> On 4/2/2013 4:59 AM, Dmitry Olshansky wrote:
>> If somebody comes up with a reasonable Visitor pattern for Exceptions
>> that is
>> flexible and fast then sure let's see it.
>
> It doesn't really need to be fast. If you need performance out of
> Exceptions, you're misusing the idiom.

The exceptions are slowish but that hardly justifies adding an extra amount of overhead on top of that. This might as well push people to avoid them even where it makes perfect sense to use exceptions.

That being said let's see that beast and then measure, then optimize and then judge it.

-- 
Dmitry Olshansky
April 02, 2013
On Tuesday, April 02, 2013 22:16:30 Dmitry Olshansky wrote:
> 02-Apr-2013 21:48, Walter Bright пишет:
> > On 4/2/2013 4:59 AM, Dmitry Olshansky wrote:
> >> If somebody comes up with a reasonable Visitor pattern for Exceptions
> >> that is
> >> flexible and fast then sure let's see it.
> > 
> > It doesn't really need to be fast. If you need performance out of Exceptions, you're misusing the idiom.
> 
> The exceptions are slowish but that hardly justifies adding an extra amount of overhead on top of that. This might as well push people to avoid them even where it makes perfect sense to use exceptions.
> 
> That being said let's see that beast and then measure, then optimize and then judge it.

D's exceptions are ridiculously slow ( http://d.puremagic.com/issues/show_bug.cgi?id=9584 ). Granted, in general, you shouldn't be relying on exceptions being efficient (they _are_ the error code path after all), but we really should do better than we're currently doing, and adding extra overhead obviously wouldn't help.

The main area that I find exception speed to be a real problem is in unit testing. Solid unit tests will test error conditions, which generally means using assertThrown to verify that the correct exception was thrown for bad input, but with how slow D's exceptions are, it becomes _very_ expensive to do many tests like that, which is very frustrating when you're trying to do thorough unit tests.

- Jonathan M Davis