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=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,c4bd2a19251049b1 X-Google-NewGroupId: yes X-Google-Attributes: gida07f3367d7,domainid0,public,usenet X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news3.google.com!feeder.news-service.com!94.75.214.39.MISMATCH!aioe.org!.POSTED!not-for-mail From: "Dmitry A. Kazakov" Newsgroups: comp.lang.ada Subject: Re: best practice: error handling Date: Tue, 31 May 2011 22:02:42 +0200 Organization: cbb software GmbH Message-ID: <1ulcworf418ai$.iy0mm08iuusy.dlg@40tude.net> References: <0b95a2a1-6e3d-4ad1-a832-e3099a9bce37@v8g2000yqb.googlegroups.com> Reply-To: mailbox@dmitry-kazakov.de NNTP-Posting-Host: ABcWHLZSxMYiZLmRr3ZBEg.user.speranza.aioe.org Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Complaints-To: abuse@aioe.org User-Agent: 40tude_Dialog/2.0.15.1 X-Notice: Filtered by postfilter v. 0.8.2 Xref: g2news1.google.com comp.lang.ada:19600 Date: 2011-05-31T22:02:42+02:00 List-Id: On Tue, 31 May 2011 11:08:38 -0700, Jeffrey Carter wrote: > On 05/31/2011 07:01 AM, milouz wrote: >> >> I'm wondering about the best way to handle errors in Ada when calling >> a function/procedure that may fails. >> >> For example, when programming on Unix systems in C, the called >> functions usually return a zero or positive value on success and -1 on >> failure, while setting a global variable (errno) to give some >> informations about the failure. > > When programming in C, returned error codes are often ignored, leading to errors > and security vulnerabilities. Global variables are never a good idea, especially > in a concurrent language. This approach should almost never be used. > > If the "error" is exceptional, then an exception should be raised. > > If the "error" is unexceptional, then a signal that cannot be ignored/misused > should be used. Exception *is* an signal, but the point correct. > For an example of the latter, consider an Index function that returns the index > of a substring ("pattern") in a larger string ("source"). It will be fairly > common for the pattern not to occur in the source, so raising an exception is > not right here. This looks like a premature optimization, but OK. > The Index functions in Ada.Strings.Fixed return Natural, with a value of zero > returned if the pattern is not found in the source. However, this can be > ignored/misused; calling code can omit the check for zero and use the returned > value as if it were a valid index into the string. Sometimes this will cause an > exception, but there are cases where it will not; such cases can be hard to find. > > The correct way to handle this is for the function to return a variant record: > > type Index_Result (Found : Boolean := False) is record > case Found is > when False => > null; > when True => > Index : Positive; > end case; > end record; > > function Index (Source : in String; Pattern : in String) return Index_Result; > > Now the signal (the discriminant) cannot be ignored, and the Index Component > cannot be misused. Again, the point is valid, but the example is not good. Zero index is as good as Index_Result. In both cases an attempt to use the index will cause an exception. So both return an "invalid index." Both solutions are equivalent and both are probably worse than exception propagation (ignoring performance issues). The problem with them is same as with IEEE floats. The "error" detection is postponed until index is actually used, while good design would be earliest possible detection. Of course much depends on the usage, i.e. where the result is supposed to be used. Actually the language lacks a construct for test-then-use. E.g. a short-cut for nasty and inefficient: if Ptr /= null then declare Object : T renames Ptr.all; begin ... use object if Key in Map then declare Element : T renames Map (Key); begin ... use element if X in S'Class then declare Y : S'Class renames S'Class (X); begin ... use it if X in Integer'Range then declare Y : Integer := Integer (X); begin ... use it and so on. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de