comp.lang.ada
 help / color / mirror / Atom feed
* best practice: error handling
@ 2011-05-31 14:01 milouz
  2011-05-31 17:13 ` Vinzent Hoefler
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: milouz @ 2011-05-31 14:01 UTC (permalink / raw)


Hi,

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.

What's the best way to deal with that kind of error in Ada ?
Throwing an exception ? Returning some error code ? Passing a special
parameter ?...




^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 14:01 best practice: error handling milouz
@ 2011-05-31 17:13 ` Vinzent Hoefler
  2011-05-31 17:57   ` AdaMagica
  2011-06-11 13:38   ` Yannick Duchêne (Hibou57)
  2011-05-31 18:08 ` Jeffrey Carter
  2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
  2 siblings, 2 replies; 15+ messages in thread
From: Vinzent Hoefler @ 2011-05-31 17:13 UTC (permalink / raw)


milouz wrote:

> I'm wondering about the best way to handle errors in Ada when calling
> a function/procedure that may fails.

Well, I'd say, there is no "best way". It depends on the application and
the precise context.

> 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.

As rule of thumb I'd suggest the following:

If the error should not occur under (almost) all sane circumstances (for
instance, waiting on a semaphore should always succeed, unless the handle
is invalid), then raise an exception.

If you kind of expect the call to fail under normal conditions, a proper
out-Parameter (like "Success" ;) can be added.

Keep in mind, that returning some error requires a function, and until now
functions can't have "out"-parameters, so returning an "error code" is only
possible for those subroutines which do not need to have "out"-parameters.
So for the sake of having a consistent interface, you might want to to use an
"out"-parameter, regardless if the subroutine in question could otherwise be
expressed as function or not.

On the other hand, if you want a function to return its result, because it
looks more natural, you cannot use an "Success : out Boolean", so it may be
more convenient to raise an exception for errors instead of using an "out"-
parameter for the result.


Vinzent.

-- 
f u cn rd ths, u cn gt a gd jb n cmptr prgrmmng.



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 17:13 ` Vinzent Hoefler
@ 2011-05-31 17:57   ` AdaMagica
  2011-06-11 13:38   ` Yannick Duchêne (Hibou57)
  1 sibling, 0 replies; 15+ messages in thread
From: AdaMagica @ 2011-05-31 17:57 UTC (permalink / raw)


On 31 Mai, 19:13, "Vinzent Hoefler"
<0439279208b62c95f1880bf0f8776...@t-domaingrabbing.de> wrote:
> On the other hand, if you want a function to return its result, because it
> looks more natural, you cannot use an "Success : out Boolean", so it may be
> more convenient to raise an exception for errors instead of using an "out"-
> parameter for the result.

Ada 2012 will add [in]out-parameters to functions.

Hope the amendment will soon be finished. Actually this was due in
April, as far as I know. But polishing the text and correcting any
errors introduced is an overwhelming job.



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 14:01 best practice: error handling milouz
  2011-05-31 17:13 ` Vinzent Hoefler
@ 2011-05-31 18:08 ` Jeffrey Carter
  2011-05-31 20:02   ` Dmitry A. Kazakov
  2011-06-11 13:48   ` Yannick Duchêne (Hibou57)
  2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
  2 siblings, 2 replies; 15+ messages in thread
From: Jeffrey Carter @ 2011-05-31 18:08 UTC (permalink / raw)


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.

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.

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.

-- 
Jeff Carter
"Clear? Why, a 4-yr-old child could understand this
report. Run out and find me a 4-yr-old child. I can't
make head or tail out of it."
Duck Soup
94



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 18:08 ` Jeffrey Carter
@ 2011-05-31 20:02   ` Dmitry A. Kazakov
  2011-05-31 20:35     ` Jeffrey Carter
  2011-06-11 13:57     ` Yannick Duchêne (Hibou57)
  2011-06-11 13:48   ` Yannick Duchêne (Hibou57)
  1 sibling, 2 replies; 15+ messages in thread
From: Dmitry A. Kazakov @ 2011-05-31 20:02 UTC (permalink / raw)


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



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 20:02   ` Dmitry A. Kazakov
@ 2011-05-31 20:35     ` Jeffrey Carter
  2011-05-31 21:40       ` Dmitry A. Kazakov
  2011-06-11 13:57     ` Yannick Duchêne (Hibou57)
  1 sibling, 1 reply; 15+ messages in thread
From: Jeffrey Carter @ 2011-05-31 20:35 UTC (permalink / raw)


On 05/31/2011 01:02 PM, Dmitry A. Kazakov wrote:
>
> 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."

Hardly. Some valid and fairly common uses include getting the part of Source 
before or after Pattern, both of which can succeed with an invalid result with 
an index of zero.

-- 
Jeff Carter
"Clear? Why, a 4-yr-old child could understand this
report. Run out and find me a 4-yr-old child. I can't
make head or tail out of it."
Duck Soup
94



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 20:35     ` Jeffrey Carter
@ 2011-05-31 21:40       ` Dmitry A. Kazakov
  0 siblings, 0 replies; 15+ messages in thread
From: Dmitry A. Kazakov @ 2011-05-31 21:40 UTC (permalink / raw)


On Tue, 31 May 2011 13:35:22 -0700, Jeffrey Carter wrote:

> On 05/31/2011 01:02 PM, Dmitry A. Kazakov wrote:
>>
>> 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."
> 
> Hardly. Some valid and fairly common uses include getting the part of Source 
> before or after Pattern, both of which can succeed with an invalid result with 
> an index of zero.

A good point. Agreed.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 14:01 best practice: error handling milouz
  2011-05-31 17:13 ` Vinzent Hoefler
  2011-05-31 18:08 ` Jeffrey Carter
@ 2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
  2011-06-11 16:12   ` Simon Wright
  2011-06-11 16:26   ` Dmitry A. Kazakov
  2 siblings, 2 replies; 15+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2011-06-11 13:31 UTC (permalink / raw)


Le Tue, 31 May 2011 16:01:19 +0200, milouz <a.michelizza@gmail.com> a  
écrit:

> Hi,
>
> 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.
>
> What's the best way to deal with that kind of error in Ada ?
> Throwing an exception ? Returning some error code ? Passing a special
> parameter ?...
I would say : at least the “errno” strategy, is not concurrency friendly.  
if two threads of a same process commit errors, then you are in trouble  
(one may overwrite the “errno” value of the other). Exceptions would be  
cleaner for such a case (providing your “errno” is not owned by threads).

Also depends on whether or not the errors you have in mind are more or  
less predictable. As two examples : a file write error is unpredictable, a  
user input error at the command line or at a use prompt, is on the  
contrary, predictable and should be supposed to be likely to happen. For  
the former, an exception would be the best in my opinion, and for the  
latter, either an exception or a result property could be OK; while I  
would personally be on favor of a result property, because a user error is  
potentially a normal result of a user input.


-- 
“Syntactic sugar causes cancer of the semi-colons.”  [Epigrams on  
Programming — Alan J. — P. Yale University]
“Structured Programming supports the law of the excluded muddle.” [Idem]
“c++; /* this makes c bigger but returns the old value */” [Anonymous]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 17:13 ` Vinzent Hoefler
  2011-05-31 17:57   ` AdaMagica
@ 2011-06-11 13:38   ` Yannick Duchêne (Hibou57)
  1 sibling, 0 replies; 15+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2011-06-11 13:38 UTC (permalink / raw)


Le Tue, 31 May 2011 19:13:16 +0200, Vinzent Hoefler  
<0439279208b62c95f1880bf0f8776eeb@t-domaingrabbing.de> a écrit:
> If you kind of expect the call to fail under normal conditions, a proper
> out-Parameter (like "Success" ;) can be added.
>
> Keep in mind, that returning some error requires a function, and until  
> now
> functions can't have "out"-parameters, so returning an "error code" is  
> only
> possible for those subroutines which do not need to have  
> "out"-parameters.
I strongly suspect this is precisely the reason why Ada 2012 will add out  
parameters to function ;)
First, I though this was a silly idea, and later, from time to time, I  
though this was indeed a good idea, at least to return status from  
function, without the need to define wrapper types every where (at least,  
while Ada do not have provision to return proper tuples like functional  
languages do). Moreover, a function may still be conceptually Pure, even  
with out parameters ;)

-- 
“Syntactic sugar causes cancer of the semi-colons.”  [Epigrams on  
Programming — Alan J. — P. Yale University]
“Structured Programming supports the law of the excluded muddle.” [Idem]
“c++; /* this makes c bigger but returns the old value */” [Anonymous]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 18:08 ` Jeffrey Carter
  2011-05-31 20:02   ` Dmitry A. Kazakov
@ 2011-06-11 13:48   ` Yannick Duchêne (Hibou57)
  1 sibling, 0 replies; 15+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2011-06-11 13:48 UTC (permalink / raw)


Le Tue, 31 May 2011 20:08:38 +0200, Jeffrey Carter  
<spam.jrcarter.not@spam.not.acm.org> a écrit:
> 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;

That's an idiom I also use with SML (I feel to remember there is a  
dedicated name for that idiom, but I forget what it is this name), as  
access to the result is automatically constrained to whether or not the  
function succeed. But while this is good for request-like function (like  
get the item with this or that property), this come to be less handy when  
many and many functions or procedure return a common type may also return  
error, because you end to as many type definition.

Another case is the if-previous-succeed-then-go-on-else-bypass idiom.  
Multiple procedure get an in/out boolen parameter. If on input, the  
parameter is False, then the procedure bypass its own process, if the  
input parameter is True, the procedure do its job and set the parameter to  
False on output if ever it fails somewhere. This is a useful idiom with  
SPARK which does not allow exceptions. Combined with that strategy, a  
wrapper type would lead to a mess.

-- 
“Syntactic sugar causes cancer of the semi-colons.”  [Epigrams on  
Programming — Alan J. — P. Yale University]
“Structured Programming supports the law of the excluded muddle.” [Idem]
“c++; /* this makes c bigger but returns the old value */” [Anonymous]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-05-31 20:02   ` Dmitry A. Kazakov
  2011-05-31 20:35     ` Jeffrey Carter
@ 2011-06-11 13:57     ` Yannick Duchêne (Hibou57)
  2011-06-11 14:07       ` Dmitry A. Kazakov
  1 sibling, 1 reply; 15+ messages in thread
From: Yannick Duchêne (Hibou57) @ 2011-06-11 13:57 UTC (permalink / raw)


Le Tue, 31 May 2011 22:02:42 +0200, Dmitry A. Kazakov  
<mailbox@dmitry-kazakov.de> a écrit:
> 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."
Disagree: an index is an index, not an error status; otherwise this is a  
kind of special-value as special-meaning (a special meaning which outside  
of the normal type semantic area). My experience says its dangerous,  
because it is always hard to read that either -1 or 0 or who know what,  
was intended to mean Error. Chance are that many people will read 0 as  
meaning “Index 0”.

-- 
“Syntactic sugar causes cancer of the semi-colons.”  [Epigrams on  
Programming — Alan J. — P. Yale University]
“Structured Programming supports the law of the excluded muddle.” [Idem]
“c++; /* this makes c bigger but returns the old value */” [Anonymous]



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-06-11 13:57     ` Yannick Duchêne (Hibou57)
@ 2011-06-11 14:07       ` Dmitry A. Kazakov
  2011-06-12  5:19         ` Randy Brukardt
  0 siblings, 1 reply; 15+ messages in thread
From: Dmitry A. Kazakov @ 2011-06-11 14:07 UTC (permalink / raw)


On Sat, 11 Jun 2011 15:57:23 +0200, Yannick Duchêne (Hibou57) wrote:

> Le Tue, 31 May 2011 22:02:42 +0200, Dmitry A. Kazakov  
> <mailbox@dmitry-kazakov.de> a écrit:
>> 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."
> Disagree: an index is an index, not an error status; otherwise this is a  
> kind of special-value as special-meaning (a special meaning which outside  
> of the normal type semantic area).

You are arguing against special value for an exception. Both Zero index and
Index_Result constrained to Found = False are special values.

> My experience says its dangerous,  
> because it is always hard to read that either -1 or 0 or who know what,  
> was intended to mean Error. Chance are that many people will read 0 as  
> meaning “Index 0”.

No, the value should be invalid for indexing and sustain other operations.
Compare it with IEEE's NaN. Zero index could have such property if we
redefined index arithmetic so that x + 0 = 0. It is possible to do
distinguishing index and offset types in arithmetic (compare with Time and
Duration), but to much headache to bother. Index_Result is simpler and
works out of the box.

BTW, I prefer exceptions to any kind of special values. But as Jeff Carter
said, there can be gray zones where special values meaning "not so
exceptional" can be used.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
@ 2011-06-11 16:12   ` Simon Wright
  2011-06-11 16:26   ` Dmitry A. Kazakov
  1 sibling, 0 replies; 15+ messages in thread
From: Simon Wright @ 2011-06-11 16:12 UTC (permalink / raw)


"Yannick Duchêne (Hibou57)" <yannick_duchene@yahoo.fr> writes:

> I would say : at least the “errno” strategy, is not concurrency
> friendly. if two threads of a same process commit errors, then you are
> in trouble (one may overwrite the “errno” value of the
> other). Exceptions would be cleaner for such a case (providing your
> “errno” is not owned by threads).

In VxWorks, errno is task-specific. (with GNAT, all Ada tasks are
VxWorks tasks, but there are VxWorks tasks which aren't Ada tasks).



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
  2011-06-11 16:12   ` Simon Wright
@ 2011-06-11 16:26   ` Dmitry A. Kazakov
  1 sibling, 0 replies; 15+ messages in thread
From: Dmitry A. Kazakov @ 2011-06-11 16:26 UTC (permalink / raw)


On Sat, 11 Jun 2011 15:31:39 +0200, Yannick Duchêne (Hibou57) wrote:

> I would say : at least the “errno” strategy, is not concurrency friendly.

But errno (and Windows' GetLastError) is not a return code. The return code
is a local object which effect is always limited to the current task, as
well as the exception (when it starts propagating). errno is a global scope
function / object, syntactically unrelated to the original call. errno is
bad even without any concurrency.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: best practice: error handling
  2011-06-11 14:07       ` Dmitry A. Kazakov
@ 2011-06-12  5:19         ` Randy Brukardt
  0 siblings, 0 replies; 15+ messages in thread
From: Randy Brukardt @ 2011-06-12  5:19 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:1835s5a847r9q$.191jzvxwbypk8.dlg@40tude.net...
...
> BTW, I prefer exceptions to any kind of special values. But as Jeff Carter
> said, there can be gray zones where special values meaning "not so
> exceptional" can be used.

I generally agree. But there are sometimes cases to the contrary. With Claw, 
we originally raised an exception when the Find routine did not find the 
menu entry, window, or whatever it was looking for. But this turned out to 
be a major pain in practice. Moreover, you can make an argument that 
"Not_Found" is not an error per-se; it is a normal result. As such, we 
changed those routines to return a special value (which does not work in 
subsequent operations). It's still possible to mistakenly use that value as 
if it was a valid object, but you'll always get an exception if you do so.

Which just shows that there is no one-size-fits-all solution to this 
problem. There is no substitute for thinking about the problem you are 
solving and how the function is going to be used.

                                 Randy.





^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2011-06-12  5:19 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-31 14:01 best practice: error handling milouz
2011-05-31 17:13 ` Vinzent Hoefler
2011-05-31 17:57   ` AdaMagica
2011-06-11 13:38   ` Yannick Duchêne (Hibou57)
2011-05-31 18:08 ` Jeffrey Carter
2011-05-31 20:02   ` Dmitry A. Kazakov
2011-05-31 20:35     ` Jeffrey Carter
2011-05-31 21:40       ` Dmitry A. Kazakov
2011-06-11 13:57     ` Yannick Duchêne (Hibou57)
2011-06-11 14:07       ` Dmitry A. Kazakov
2011-06-12  5:19         ` Randy Brukardt
2011-06-11 13:48   ` Yannick Duchêne (Hibou57)
2011-06-11 13:31 ` Yannick Duchêne (Hibou57)
2011-06-11 16:12   ` Simon Wright
2011-06-11 16:26   ` Dmitry A. Kazakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox