From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Thread: 103376,7d3cb5920e882220 X-Google-Attributes: gid103376,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news3.google.com!feeder1-2.proxad.net!proxad.net!feeder1-1.proxad.net!ecngs!feeder2.ecngs.de!newsfeed.freenet.de!bolzen.all.de!newsfeed.ision.net!newsfeed2.easynews.net!ision!citadel.nobulus.com!fi.sn.net!newsfeed1.fi.sn.net!news.song.fi!not-for-mail Date: Mon, 10 Dec 2007 22:02:55 +0200 From: Niklas Holsti User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.8) Gecko/20060628 Debian/1.7.8-1sarge7.1 X-Accept-Language: en MIME-Version: 1.0 Newsgroups: comp.lang.ada Subject: Re: Exceptions References: <5947aa62-2547-4fbb-bc46-1111b4a0dcc9@x69g2000hsx.googlegroups.com> <475c6ed8$0$13111$9b4e6d93@newsspool2.arcor-online.net> <1kxk3hlfa25dw$.fl2wvbn0tpbg$.dlg@40tude.net> <475d296a$0$27813$4f793bc4@news.tdc.fi> <12mjar2f2t2e6$.o2upq0n29j1f.dlg@40tude.net> In-Reply-To: <12mjar2f2t2e6$.o2upq0n29j1f.dlg@40tude.net> Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Message-ID: <475d99c6$0$3520$4f793bc4@news.tdc.fi> Organization: TDC Song Internet Services NNTP-Posting-Host: laku61.adsl.netsonic.fi X-Trace: 1197316550 news.tdc.fi 3520 81.17.205.61:32863 X-Complaints-To: abuse@tdcnet.fi Xref: g2news1.google.com comp.lang.ada:18864 Date: 2007-12-10T22:02:55+02:00 List-Id: Dmitry A. Kazakov wrote: > On Mon, 10 Dec 2007 14:09:31 +0200, Niklas Holsti wrote: > > >>Dmitry A. Kazakov wrote: >> >> > [some questions, see below] >> >>I'm not sure if Dmitry really wants answers to his questions below >>-- perhaps they were merely rhetorical -- but they tickled my >>curiosity, so here is one data point from my main Ada application: > > > Thank you for the figures. It is really interesting. > > >>>1. How often do you declare a new exception when writing a new subprogram? >>>My guess is about 1 / 10_000. >> >> From the numbers above: 87 / 6_292 = 138 / 10_000. > > > One per hundred also. As for me I tend to reuse IO_Exceptions if semantics > is close, so my figure should be much lesser. The only language-defined exceptions that are thusly "reused" in this application are Constraint_Error (mainly used in container structures that have some notion of "indexing" or "keyed look-up" where C_E is used to report an invalid index or key) and Program_Error (to kill the application when its state is screwed up in a way that indicates a programming error rather than an input error or user error). One reason for the large number of application-defined exceptions in this application may be that it uses a lot of functional-style programming with functions that return objects with run-time constraints (eg. unconstrained array types). Such functions cannot easily use error codes and must use exceptions to signal problems. >>In some cases >>one declared exception can be raised in several subprograms, but I >>think the average number of subprograms that can raise a given >>application-defined exception is between 1 and 2, not larger, in >>this application. > > This is a very low figure. It is my estimate for the *average* number of subprograms that contain raise statements for a given application-defined exception. There are certainly *some* exceptions that are raised in 5..10 subprograms. The number of subprograms that can *propagate* a given application-defined exception (from their callees) is larger, but I can't say by how much. Clearly most subprograms have no exception handlers at all and thus propagate all exceptions raised in them or in their callees. > But as I said, I reuse exceptions much. A > possible danger is to overload an exception too much (like Constraint_Error > already is). Agreed. Whenever this application explicitly raises Constraint_Error it is (or should be) handled a few (1 .. 3) levels higher in the call path. Several handlers for Constraint_Error (whether raised explicitly or by standard run-time checks) "translate" C_E to a more specific exception by raising the specific application-defined exception for handling at higher levels. > This is IMO a question of custom and also of the language design. When > exceptions tend to propagate out of the application, one would certainly > like to make them "meaningful" indicating the fault reason as precise as > possible. Yes, but whatever one does, an exception occurrence is almost never meaningful for the user, only for the developer. > This is not what I wanted from Ada. I'd like to have all > exceptions handled. So I treat them rather as signals within and for the > application. If I can foresee a situation that can cause a certain exception, of course I put in a handler. The problem is programming errors or input errors that I do not foresee. For such I still rely on catch-all handlers at certain levels and ask the user to send me the error messages that these handlers emit. > I don't buy exceptions as a debugging tool. Exceptions (even when unhandled) are an essential part of my debugging kit, but of course not the only part. In the application under discussion an unhandled (or "others" handled) exception typically only indicates the presence of a programming error, not its location or its nature. For the rest, I tend to use programmed internal checks and action/state traces that can be turned on with specific command-line options. I rarely use a real debugger. >>>2. How often does a caller "eat" an exception of the callee instead of >>>propagating it further? My guess is 1 / 10. >> >>My numbers do not separate between exceptions propagated from calls >>and exceptions raised within the handled sequence-of-statements >>itself. But my feeling is that at least half of the handlers are >>meant to catch exception from calls, and I don't think that there >>is any systematic difference in the proportion of "eaters" versus >>"propagators" between the two cases. >> >>So, from the above numbers, "eaters" / "all handlers" = 180 / 275 = >> 6.5 / 10. > > > Interesting. I didn't measured my code, but I expect more scopes where > exceptions propagate unhandled. Sure, I was just comparing *handlers* that "eat" versus handlers that "propagate". Most subprograms have no handlers and propagate all exceptions. In another post, you (Dmitry) say that your question (2) meant the number of call levels over which an exception usually propagates before being handled -- the "distance" between the raise point and the handler. I haven't measured that for my application, but my feeling is that there are two kinds of exception (occurrences): the non-fatal ones and the fatal or semi-fatal ones. For a non-fatal exception the application takes some corrective action, for example throws away some small unit of data or increases the size of a container structure, and then continues processing. In this application the raise-to-handle distance for such exceptions is small, 1..3 levels I would say. For a fatal or semi-fatal exception this application either stops completely or skips a major part of its input and continues with the remaining parts. Here the raise-to-handle distance is large and the handler is often an "others" handler. > That depends on how much refactoring is > done. Actually, I have an impression that many exception-related bugs > appear while code refactoring and other small apparently equivalent code > modifications, which in turn are not almost equivalent. This app has had only a handful of exception-related bugs, most coming from null accesses, a couple from overflows (the app handles machine-level binary numbers of various sizes). I can't say if they are related to refactoring of any sort. >>Good error tolerance may require more local handlers to >>clean up local state, before (possibly) passing the exception to >>higher levels. > > Possibly, however there is a concurring mechanism for clean-ups, I mean > controlled types. In many cases Finalize does what otherwise a handler > would. True. This app was started when Ada 95 was young, and we were a bit afraid of compiler bugs in controlled types, so almost no controlled types are used (Unbounded_String is used in several places, though). >>The application behind the numbers given above is not required to >>be error-tolerant. Thus, many exceptions can simply be propagated >>to a high level, without passing through many local clean-up >>handlers on the way. (Commenting on my own text above: these are the "fatal or semi-fatal" exceptions.) > Yes, but again it is probably not about being error-[in]tolerant. I think > the overall code structure plays a role here. When I refactor small > subprogram doing this or that thing, I often leave exception handling > functionality to another piece of code. Maybe this is my personal > motivation behind longing for contracted exceptions. The main problem I see in contracted exceptions is the problem with layered architectures where some intermediate layers are general/generic and not application-specific. For example, a generic tree-traversal (intermediate) layer may have to propagate application-defined exceptions from the application-specific "action" routines in a lower layer, to the application's handlers in a higher layer. I don't see how to do that easily with explicit exception contracts. It may be easier in a language with exception classes that can be derived into exception subclasses that have application-defined occurrence-specific data components. The intermediate layer could define its own exception class(es) for such propagated exceptions and the application would define subclass(es) with application-specific information. The contract of the intermediate layer could the list the exception class(es), not the subclass(es). -- Niklas Holsti Tidorum Ltd niklas holsti tidorum fi . @ .