Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #18090 > unrolled thread

Mechanism to generate annotated "error codes"

Started byDon Y <this@isnotme.com>
First post2012-03-01 12:48 -0700
Last post2012-03-06 11:21 -0700
Articles 20 on this page of 87 — 17 participants

Back to article view | Back to comp.lang.c


Contents

  Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-01 12:48 -0700
    Re: Mechanism to generate annotated "error codes" Willem <willem@toad.stack.nl> - 2012-03-01 21:29 +0000
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-01 16:53 -0700
    Re: Mechanism to generate annotated "error codes" William Ahern <william@wilbur.25thandClement.com> - 2012-03-01 14:13 -0800
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-01 22:07 -0700
    Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-01 17:53 -0500
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-01 23:05 -0700
        Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-02 01:50 -0500
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 00:43 -0700
            Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-03 17:58 -0500
              Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-04 03:16 -0500
                Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 13:47 -0700
              Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 13:45 -0700
                Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-04 18:37 -0500
                  Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 22:12 -0700
                    Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-08 01:41 -0500
                      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-09 14:34 -0700
    Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 00:30 -0700
      Re: Mechanism to generate annotated "error codes" Keith Thompson <kst-u@mib.org> - 2012-03-02 02:05 -0800
        Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-02 10:47 +0000
        Re: Mechanism to generate annotated "error codes" gazelle@shell.xmission.com (Kenny McCormack) - 2012-03-02 14:18 +0000
        Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 10:46 -0700
          Re: Mechanism to generate annotated "error codes" James Kuyper <jameskuyper@verizon.net> - 2012-03-02 14:08 -0500
            Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 12:38 -0700
          Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-02 22:57 +0000
            Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 17:14 -0700
              Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-03 02:25 +0000
              Re: Mechanism to generate annotated "error codes" James Kuyper <jameskuyper@verizon.net> - 2012-03-03 06:10 -0500
                Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-03 16:12 -0500
          Re: Mechanism to generate annotated "error codes" Keith Thompson <kst-u@mib.org> - 2012-03-03 04:04 -0800
            Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-03 15:05 +0000
              Re: Mechanism to generate annotated "error codes" Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-03-03 16:25 +0000
            Re: Mechanism to generate annotated "error codes" Shao Miller <sha0.miller@gmail.com> - 2012-03-03 16:26 -0500
              Re: Mechanism to generate annotated "error codes" Keith Thompson <kst-u@mib.org> - 2012-03-03 13:40 -0800
        Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-03 12:03 +1300
          Re: Mechanism to generate annotated "error codes" Keith Thompson <kst-u@mib.org> - 2012-03-02 15:45 -0800
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-02 17:18 -0700
        Re: Mechanism to generate annotated "error codes" blp@cs.stanford.edu (Ben Pfaff) - 2012-03-02 15:08 -0800
        Re: Mechanism to generate annotated "error codes" "BartC" <bc@freeuk.com> - 2012-03-03 10:30 +0000
          Re: Mechanism to generate annotated "error codes" James Kuyper <jameskuyper@verizon.net> - 2012-03-03 06:19 -0500
          Re: Mechanism to generate annotated "error codes" Phil Carmody <thefatphil_demunged@yahoo.co.uk> - 2012-03-03 15:15 +0200
          Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-03 14:52 +0000
            Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-03 07:39 -0800
              Re: Mechanism to generate annotated "error codes" Jonathan Leffler <jleffler@earthlink.net> - 2012-03-03 23:56 -0800
                Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-05 09:20 +1300
                  Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-04 13:28 -0800
                    Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-05 10:56 +1300
            Re: Mechanism to generate annotated "error codes" "BartC" <bc@freeuk.com> - 2012-03-03 17:02 +0000
          Re: Mechanism to generate annotated "error codes" Keith Thompson <kst-u@mib.org> - 2012-03-03 12:30 -0800
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 19:15 -0700
        Re: Mechanism to generate annotated "error codes" Kaz Kylheku <kaz@kylheku.com> - 2012-03-04 03:20 +0000
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 11:03 -0700
        Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 16:31 -0700
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 23:04 -0700
    Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-02 22:13 -0800
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 00:49 -0700
        Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-03 03:32 -0800
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 12:04 -0700
            Re: Mechanism to generate annotated "error codes" Lowell Gilbert <lgusenet@be-well.ilk.org> - 2012-03-03 15:06 -0500
              Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 14:38 -0700
            Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-03 12:15 -0800
              Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 16:51 -0700
                Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-04 14:08 -0800
                  Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 15:56 -0700
        Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-03 03:45 -0800
          Re: Mechanism to generate annotated "error codes" Lowell Gilbert <lgusenet@be-well.ilk.org> - 2012-03-03 14:11 -0500
            Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 13:03 -0700
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 12:32 -0700
        Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 15:04 -0700
          Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-04 11:09 +1300
            Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 16:55 -0700
              Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-04 13:04 +1300
                Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 18:24 -0700
          Re: Mechanism to generate annotated "error codes" Malcolm McLean <malcolm.mclean5@btinternet.com> - 2012-03-03 14:30 -0800
            Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-03 17:06 -0700
    Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-05 10:09 +1300
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 16:03 -0700
        Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-05 13:26 +1300
          Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-04 22:21 -0700
            Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-05 20:46 +1300
              Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-05 10:54 -0700
                Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-06 10:02 +1300
                  Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-05 16:09 -0700
              Re: Mechanism to generate annotated "error codes" "BartC" <bc@freeuk.com> - 2012-03-06 10:54 +0000
                Re: Mechanism to generate annotated "error codes" Ian Collins <ian-news@hotmail.com> - 2012-03-07 07:22 +1300
    Re: Mechanism to generate annotated "error codes" gordonb.8sa24@burditt.org (Gordon Burditt) - 2012-03-06 08:51 -0600
      Re: Mechanism to generate annotated "error codes" Don Y <this@isnotme.com> - 2012-03-06 11:21 -0700

Page 3 of 5 — ← Prev page 1 2 [3] 4 5  Next page →


#18142

FromPhil Carmody <thefatphil_demunged@yahoo.co.uk>
Date2012-03-03 15:15 +0200
Message-ID<87ipilltbd.fsf@bazspaz.fatphil.org>
In reply to#18136
"BartC" <bc@freeuk.com> writes:
> "Keith Thompson" <kst-u@mib.org> wrote in message
> news:lnr4xb71y6.fsf@nuthaus.mib.org...
> > Don Y <this@isnotme.com> writes:
> > [...]
> >> You need to maintain this "by hand".  There is nothing that
> >> prevents you from:
> >>    #define YES  (2)
> >>    #define NO   (2)
> >>
> >> Nor:
> >>    #define YES (2)
> >>    ...
> >>    #define ABSOLUTELY  (27)
> >> (assuming yes and absolutely are synonyms)
> > [...]
> >
> > A minor point: parenthesizing macro definitions is a good habit, but
> > it's not necessary when the macro expands to a single token.  This:
> >
> >    #define ABSOLUTELY 27
> >
> > is just as safe as
> >
> >    #define ABSOLUTELY (27)
> 
> What happens when someone experiments with the value 27 by trying this:
> 
>     #define ABSOLUTELY 27+1

Exactly the same thing that happens when someone experiments with the value
27 by trying this:

     #define ABSOLUTELY (27)+1

Such arguments are called "Straw Men".

Phil
-- 
> I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)

[toc] | [prev] | [next] | [standalone]


#18145

FromKaz Kylheku <kaz@kylheku.com>
Date2012-03-03 14:52 +0000
Message-ID<20120303062257.175@kylheku.com>
In reply to#18136
On 2012-03-03, BartC <bc@freeuk.com> wrote:
> "Keith Thompson" <kst-u@mib.org> wrote in message 
> news:lnr4xb71y6.fsf@nuthaus.mib.org...
>> Don Y <this@isnotme.com> writes:
>> [...]
>>> You need to maintain this "by hand".  There is nothing that
>>> prevents you from:
>>>    #define YES  (2)
>>>    #define NO   (2)
>>>
>>> Nor:
>>>    #define YES (2)
>>>    ...
>>>    #define ABSOLUTELY  (27)
>>> (assuming yes and absolutely are synonyms)
>> [...]
>>
>> A minor point: parenthesizing macro definitions is a good habit, but
>> it's not necessary when the macro expands to a single token.  This:
>>
>>    #define ABSOLUTELY 27
>>
>> is just as safe as
>>
>>    #define ABSOLUTELY (27)
>
> What happens when someone experiments with the value 27 by trying this:
>
>     #define ABSOLUTELY 27+1

That same someone might experiment with (27) this way:

   #define ABSOLUTELY (27)+1

You can't possibly be suggesting that when you're coding, you're not only
responsible for what you're actually writing, but also for all possible
careless or clueless mutations of it by someone else?

If you're thinking that this someone is actually yourself, later, then change
the habits of that someone. Don't experiment with a constant 27 by changing it
to 27+1, but just replace it with 28. Always use some kind of version control
which can tell you at any time where your experimental changes are, so
you don't need to use +1 as a flag denoting an experimental change, or
to remind you of what the stock value is.

Secondly, acquire a smarter habit: that of ensuring that every macro
definition you touch, which produces an expression, produces a primary
expression. 27 is already a primary expression, so needs nothing.
21+1 is not a primary expression, and so it needs parentheses, or a
rewrite to 28.

Even the habit of "always add parentheses" is still conditional on something!
Because the dummy who does this will surely not add parentheses every time an
edit is made, like an automaton. E.g.

  #define ABSOLUTELY ((27)+1) /* experiment */

  #define ABSOLUTELY (((27)+1)+1) /* another experiment */

Even the always-parenthesizing dummy still recognizes that the expression is
already parenthesized (and that experiments can go into the parentheses).

If your habit is conditional anyway, you might as well choose a smart condition
for it: "if expr not already primary, make primary".

[toc] | [prev] | [next] | [standalone]


#18148

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-03-03 07:39 -0800
Message-ID<544325ad-fe57-4eeb-97de-1ac62964022b@b18g2000vbz.googlegroups.com>
In reply to#18145
On Mar 3, 2:52 pm, Kaz Kylheku <k...@kylheku.com> wrote:
>
>   #define ABSOLUTELY ((27)+1) /* experiment */
>
>   #define ABSOLUTELY (((27)+1)+1) /* another experiment */
>
> Even the always-parenthesizing dummy still recognizes that the expression is
> already parenthesized (and that experiments can go into the parentheses).
>
Too many parentheses make code hard to read.

double clamp(double x, double low, double high)
{
  if(x < low)
    return low;
  if(x > high)
     return high;
  return x;
}

#define clamp(x,low,high) x < low ? low : x > high ? high : x

#define clamp(x, low, high) ((x) < (low) ? (low) : (x) > (high) ?
(high) : (x))

The last is a lot harder to read than the first.

--
Basic Algorithms: a fantastic compendium of C resources
See my website http://www.malcolmmclean.site11.com/www


[toc] | [prev] | [next] | [standalone]


#18192

FromJonathan Leffler <jleffler@earthlink.net>
Date2012-03-03 23:56 -0800
Message-ID<l8CdnWERKs7avc7SnZ2dnUVZ_qmdnZ2d@earthlink.com>
In reply to#18148
On 3/3/12 7:39 AM, Malcolm McLean wrote:
> On Mar 3, 2:52 pm, Kaz Kylheku<k...@kylheku.com>  wrote:
>>
>>    #define ABSOLUTELY ((27)+1) /* experiment */
>>
>>    #define ABSOLUTELY (((27)+1)+1) /* another experiment */
>>
>> Even the always-parenthesizing dummy still recognizes that the expression is
>> already parenthesized (and that experiments can go into the parentheses).
>>
> Too many parentheses make code hard to read.
>
> double clamp(double x, double low, double high)
> {
>    if(x<  low)
>      return low;
>    if(x>  high)
>       return high;
>    return x;
> }
>
> #define clamp(x,low,high) x < low ? low : x > high ? high : x
>
> #define clamp(x, low, high) ((x) < (low) ? (low) : (x) > (high) ?
> (high) : (x))
>
> The last is a lot harder to read than the first.

Regardless of the readability, the main merit of the fully parenthesized 
macro is that it works as expected even if used like:

x = clamp(x | 0x0F, a & 0xF0, b ^ 0xA5) + 12;

The macro without the parentheses does not - and is therefore unsafe, 
however much more 'readable' it is.  (Granted, GCC warns about all sorts 
of issues with the unparenthesized version, but those are best fixed by 
parentheses in the macro definition and not by requiring parentheses in 
the invocation.

Given x, a and b each ranging over the range 0..255, there are 30976 out 
of 16777216 sets of values where the two clamp macros yield the same result.

#include <stdio.h>

#define clamp1(x,low,high) x < low ? low : x > high ? high : x
#define clamp2(x, low, high) ((x) < (low) ? (low) : (x) > (high) ? 
(high) : (x))

int main(void)
{
     for (int x = 0; x < 256; x++)
     {
         for (int a = 0; a < 256; a++)
         {
             for (int b = 0; b < 256; b++)
             {
                 int y = clamp1(x | 0x0F, a & 0xF0, b ^ 0xA5) + 12;
                 int z = clamp2(x | 0x0F, a & 0xF0, b ^ 0xA5) + 12;
                 if (y != z)
                     printf("x=%.2X, a=%.2X, b=%.2X, y=%.2X, z=%.2X,\n",
                            x, a, b, y, z);
             }
         }
     }
}

I don't think it is a valid excuse to say "but clamp() is supposed to be 
used with double arguments"; macros are not type aware.

-- 
Jonathan Leffler <jleffler@earthlink.net>
#include <disclaimer.h>

[toc] | [prev] | [next] | [standalone]


#18202

FromIan Collins <ian-news@hotmail.com>
Date2012-03-05 09:20 +1300
Message-ID<9ri14mFaddU1@mid.individual.net>
In reply to#18192
On 03/ 4/12 08:56 PM, Jonathan Leffler wrote:
> On 3/3/12 7:39 AM, Malcolm McLean wrote:
>> On Mar 3, 2:52 pm, Kaz Kylheku<k...@kylheku.com>   wrote:
>>>
>>>     #define ABSOLUTELY ((27)+1) /* experiment */
>>>
>>>     #define ABSOLUTELY (((27)+1)+1) /* another experiment */
>>>
>>> Even the always-parenthesizing dummy still recognizes that the expression is
>>> already parenthesized (and that experiments can go into the parentheses).
>>>
>> Too many parentheses make code hard to read.
>>
>> double clamp(double x, double low, double high)
>> {
>>     if(x<   low)
>>       return low;
>>     if(x>   high)
>>        return high;
>>     return x;
>> }
>>
>> #define clamp(x,low,high) x<  low ? low : x>  high ? high : x
>>
>> #define clamp(x, low, high) ((x)<  (low) ? (low) : (x)>  (high) ?
>> (high) : (x))
>>
>> The last is a lot harder to read than the first.
>
> Regardless of the readability, the main merit of the fully parenthesized
> macro is that it works as expected even if used like:
>
> x = clamp(x | 0x0F, a&  0xF0, b ^ 0xA5) + 12;
>
> The macro without the parentheses does not - and is therefore unsafe,
> however much more 'readable' it is.  (Granted, GCC warns about all sorts
> of issues with the unparenthesized version, but those are best fixed by
> parentheses in the macro definition and not by requiring parentheses in
> the invocation.
>
> Given x, a and b each ranging over the range 0..255, there are 30976 out
> of 16777216 sets of values where the two clamp macros yield the same result.
>
> #include<stdio.h>
>
> #define clamp1(x,low,high) x<  low ? low : x>  high ? high : x
> #define clamp2(x, low, high) ((x)<  (low) ? (low) : (x)>  (high) ?
> (high) : (x))
>
> int main(void)
> {
>       for (int x = 0; x<  256; x++)
>       {
>           for (int a = 0; a<  256; a++)
>           {
>               for (int b = 0; b<  256; b++)
>               {
>                   int y = clamp1(x | 0x0F, a&  0xF0, b ^ 0xA5) + 12;
>                   int z = clamp2(x | 0x0F, a&  0xF0, b ^ 0xA5) + 12;
>                   if (y != z)
>                       printf("x=%.2X, a=%.2X, b=%.2X, y=%.2X, z=%.2X,\n",
>                              x, a, b, y, z);
>               }
>           }
>       }
> }
>
> I don't think it is a valid excuse to say "but clamp() is supposed to be
> used with double arguments"; macros are not type aware.

Yet another reason not to use function like macros.

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#18207

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-03-04 13:28 -0800
Message-ID<c56cd607-b47b-48d6-90e9-6f9807d6ddcc@1g2000yqv.googlegroups.com>
In reply to#18202
On Mar 4, 8:20 pm, Ian Collins <ian-n...@hotmail.com> wrote:
> On 03/ 4/12 08:56 PM, Jonathan Leffler wrote:
>
> > #include<stdio.h>
>
> > #define clamp1(x,low,high) x<  low ? low : x>  high ? high : x
> > #define clamp2(x, low, high) ((x)<  (low) ? (low) : (x)>  (high) ?
> > (high) : (x))
>
> Yet another reason not to use function like macros.
>
The problem is that clamp() is very likely to be in an inner loop. For
instance you might be ensuring that the pixels you write after merging
two images are in the range 0-255. So the function version that
operates on doubles isn't adequate, unless you can really trust your
compiler with aggressive optimisations.

--
Basic Algorithms. A fantastic C programming resource
http://www.malcolmmclean.site11.com/www


[toc] | [prev] | [next] | [standalone]


#18208

FromIan Collins <ian-news@hotmail.com>
Date2012-03-05 10:56 +1300
Message-ID<9ri6paFadbU4@mid.individual.net>
In reply to#18207
On 03/ 5/12 10:28 AM, Malcolm McLean wrote:
> On Mar 4, 8:20 pm, Ian Collins<ian-n...@hotmail.com>  wrote:
>> On 03/ 4/12 08:56 PM, Jonathan Leffler wrote:
>>
>>> #include<stdio.h>
>>
>>> #define clamp1(x,low,high) x<    low ? low : x>    high ? high : x
>>> #define clamp2(x, low, high) ((x)<    (low) ? (low) : (x)>    (high) ?
>>> (high) : (x))
>>
>> Yet another reason not to use function like macros.
>>
> The problem is that clamp() is very likely to be in an inner loop. For
> instance you might be ensuring that the pixels you write after merging
> two images are in the range 0-255. So the function version that
> operates on doubles isn't adequate, unless you can really trust your
> compiler with aggressive optimisations.

s/aggressive/trivial/

-- 
Ian Collins

[toc] | [prev] | [next] | [standalone]


#18151

From"BartC" <bc@freeuk.com>
Date2012-03-03 17:02 +0000
Message-ID<jitirp$48p$1@dont-email.me>
In reply to#18145
"Kaz Kylheku" <kaz@kylheku.com> wrote in message 
news:20120303062257.175@kylheku.com...
> On 2012-03-03, BartC <bc@freeuk.com> wrote:
>> "Keith Thompson" <kst-u@mib.org> wrote in message

>>>    #define ABSOLUTELY 27
>>>
>>> is just as safe as
>>>
>>>    #define ABSOLUTELY (27)
>>
>> What happens when someone experiments with the value 27 by trying this:
>>
>>     #define ABSOLUTELY 27+1
>
> That same someone might experiment with (27) this way:
>
>   #define ABSOLUTELY (27)+1

In the case of (27), it's far more likely that it would be written as 
(27+1).

In practice, everyone writes 27, and expects anyone modifying it to insert 
any parentheses needed. But (27) is still marginally safer than just 27 for 
those few cases where it doesn't happen.

-- 
Bartc 

[toc] | [prev] | [next] | [standalone]


#18159

FromKeith Thompson <kst-u@mib.org>
Date2012-03-03 12:30 -0800
Message-ID<lneht977h0.fsf@nuthaus.mib.org>
In reply to#18136
"BartC" <bc@freeuk.com> writes:
> "Keith Thompson" <kst-u@mib.org> wrote in message
> news:lnr4xb71y6.fsf@nuthaus.mib.org...
[...]
>> A minor point: parenthesizing macro definitions is a good habit, but
>> it's not necessary when the macro expands to a single token.  This:
>>
>>    #define ABSOLUTELY 27
>>
>> is just as safe as
>>
>>    #define ABSOLUTELY (27)
>
> What happens when someone experiments with the value 27 by trying this:
>
>    #define ABSOLUTELY 27+1

It breaks.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#18183

FromDon Y <this@isnotme.com>
Date2012-03-03 19:15 -0700
Message-ID<jiuj7v$uam$1@speranza.aioe.org>
In reply to#18104
Hi Stefan,

On 3/2/2012 8:05 PM, Stefan Ram wrote:
> Don Y<this@isnotme.com>  writes:
>> You need to maintain this "by hand".  There is nothing that
>> prevents you from:
>>    #define YES  (2)
>>    #define NO   (2)
>
>    The include-file can be generated:
>
> printf( "#define YES (%d)\n", ++i );

Sure!  But how do you know "YES" is ever USED anywhere?
(replace YES with MOTOR_OVERHEATED and every other
"error" condition that this file IMPLIES exists)

Also, you have now created a dependency that "every"
file relies upon.  Add a new error code and every file
that #includes "errorcodes.h" has to be recompiled.

You also tempt people to do things like:

    /* errors generated by get_value() */
    #define TOO_MANY_DECIMAL_POINTS (27)
    #define MISSING_SIGN            (28)
    #define LOST_PRECISION          (29)

    /* errors generated by run_motor() */
    ...

    if ( (error >= TOO_MANY_DECIMAL_POINTS)
         && (error <= LOST_PRECISION) ) {
        // this must be an error propagated up from get_value()

So, if you later add a TOO_MANY_SIGNS error code, you risk
breaking this code (which should never have existed in the
first place).  Error codes aren't "numbers" that can be
manipulated as such.  They have no relationships to each
other.

>> There is nothing that ensures:
>> #define TOO_MANY_DIGITS   (375)
>> is actually *used* anywhere!
>
>    This is a kind of library. When you defined anything (say, a
>    function) in a library, you can never know whether the
>    client really uses it.

In *my* scheme, errors that are never signaled magically disappear
from the "error space" -- just like opting not to link in a
routine from a library.

>> You have to maintain one such table for each library/subsystem
>> (having one for the entire application is just silly -- if it
>> has to be maintained by hand!)
>
>    I only see two extremes: Either maintain one table per
>    function (which I do), or one global table for all your
>    code (like Microsofts HRESULTs, see
>
> http://source.winehq.org/source/include/winerror.h
>
>    ). You seem to prefer some kind of mixture: one table
>    per subsystem. This is confusing since now merging, splitting
>    or changing subsystem boundaries will become more difficult,
>    because one also needs to adjust this table each time.

You have those problems IF YOU WANT TO MAINTAIN THAT TABLE!
*I* don't have "that table"!  :>

>> And, if you have multiple such tables, you have to ensure the
>> "number spaces" for each do not overlap.
>
>    If you do not want them to overlap, then you have
>    effectively one single global table per programmer (=Don Y).

No.  No table.  Period.

>    What happens when you join another programmer or
>    organization for a project? Will they agree to use your
>    table? Will they force you to use their error-code
>    convention?

That's a management issue.  Another programmer joins your team.
Does your Management insist on coding standards and procedures?
If not, then, "Hey, this is how *my* code reports errors.  I
don't really care how *yours* does -- except to the extent
that I use yours.  Demonstrate how your scheme handles these
issues and I'll show you how MINE does..."

>> How do you find which errors are thrown by each module/library/etc.?
>
>    grep?

Sure.  So you examine the entire source tree each time you
want to locate the source of an error?  How do you guarantee
that get_isbn_number() doesn't use "TOO_MANY_DECIMAL_POINTS"
to indicate a malformed ISBN?  What do you use to find error
code "instances" in the source tree?  Is "TOO_MANY_POTATOES"
an error code?

>> How do you ensure you don't already have "OVERFLOW_DOUBLE" hidden
>> away in that SINGLE long list of error codes?
>
>    You add tags in comments
>
> #define OVERFLOW_DOUBLE 2 /* tag:numeric tag:iserror */
>
>    Then you can find all numeric error codes with grep.

Again, you are having to do this MANUALLY.  I'm sorry but *I*
am not infallible  :>  And, most of the folks I've worked with
are no better than me!

>> No!  That's what leads to "bad value" type error messages!
>
>    If it is programmed in this way.
>
>> The application doesn't want to be concerned with the details of
>> "what makes a particular input string a valid numeric indicator".
>> It wants to *bury* that information and those criteria inside
>> a tool that it then *uses* to perform that operation for it.
>
>    Such a tool cannot know the domain vocabulary of the
>    application.

The tool doesn't have to know the vocabulary.  It provides a means
for the *developer* to write the messages.  All it does is bind
error codes to messages, ensure that those error codes are
unique, indicates where each is raised in the source tree and
give a handle to this information back to the devloper (and
the code he writes)

>    Usually, an application calls a library function, which
>    returns application-domain independent error codes. The
>    application then translates these into error messages which
>    use the language (domain vocabulary and natural language)
>    of the user of the application.

Exactly.  As you move up the abstraction hierarchy, you apply
more context to your messages.  "Bad value" becomes "Bad house
number" (as in a street address).  The advantage is that an
upper layer of abstraction can get a description of the
exact nature of the "problem" instead of just treating it as
a "bad number" (what, specifically, *makes* it a "bad number"?)

>> Should the UI be responsible for identifying the reason why
>> keystrokes are not available from the "keyboard device"? That
>> would imply that the UI would need to know *how* the keyboard
>> device works and the sorts of things that could go wrong with
>> it.
>
>    When there are no keystrokes, it usually means that no one
>    has pressed a key, which does not seem to be an error. The
>    computer usually waits patiently.

Or the keyboard is unplugged.  Or the pty connected to stdin
has disconnected.  Or...

>> Likewise, should a function that gets the setpoint for a
>> temperature controller from the user know what the *actual*
>> syntax of a "valid temperature specification" is?  And,
>> *how* the user erred in specifying that value?
>
>    When the temperatur controller defines this syntax
>    for its interface, it does not have to be the same syntax
>    that is used in the user interface of the application.

The temperature is an *aspect* of the application (not a hardware
device, etc.).  It would be silly to expect users to specify
temperatures using roman numerals when all other numeric values
are specified in decimal.

I.e., "get_value()" would tend to be an implicit part of the
design of the user interface.  You wouldn't want the user to
learn different rules for different parts of the application.

>    The application defines the syntax it expects from its
>    user. It then checks this syntax reporting any errors
>    to the user.

You don't *really* parse the user input, keystroke by keystroke,
in your main(), do you??  No atof(), strtod(), sscanf(), etc.?

>    If there are no errors, it converts this
>    syntax into whatever syntax the temperatur controller
>    uses. (We do not want the user to learn a new application
>    syntax, whenever the temperatur controller was exchanged.)
>
>    If the temperature controller then reports any syntax
>    errors, that's an error of the application programmer,
>    not of the user.
>
>> The application *layer* that is responsible for this is
>> where the error can best be explained.  Anything below that
>> doesn't have sufficient knowledge of *context* to explain
>> it meaningfully.  Anything *above* that doesn't want to be
>> concerned with this level of detail (see above).
>
>    I do not see anything above the application layer. The
>    application is the highest layer that communicates directly
>    with the user. It uses a layer below, which is the library
>    layer.

There are *many* layers.  Within libraries, between libraries,
within subsystems, between subsystems, etc.  Any of these can
be designed to talk to the user.

E.g., main() calls OpenFileDialog() to get a filename from
the user.  OpenFileDialog() is "communicating directly with the
user" -- yet it is NOT at the top/application layer!

>> And how does the person *using* the application that encapsulates
>> this code know what's gone wrong with his *use* of the application?
>
>    It is the duty of the application to explain that to him.
>
>> filemove exposes the error from MoveFile to upper levels.
>
>    No, it exposes its own codes. When they happen to be the
>    same as the codes of a lower level, this is a coincidence.
>    For example say:
>
> int f(){ return g(); }
>
>    here, f returns its own codes, converting the codes it
>    got from g, even if, in this special case, the conversion
>    happens to be simple, because it is the identity function,
>    by pure coincidence.
>
>> How do you explain the error to the user -- unless you
>> require MoveFile to be aware of all of the things that can
>> happen *inside* filemove?
>
>    You have to translate them anyway, because all text
>    messages of libraries usually use a specific natural
>    language, say: English.
>
>    Now, as we all know, users typically do not understand
>    English but German. Well, whatever language they understand:
>    The library does not need to know. A file-copy-library
>    should have to know about natural languages.
>
>    Yes, the application must translate each and every little error
>    message into a language that the user understands, including
>    his domain vocabulary. As they say:
>
>        »Writing an application is a dirty job,
>        but somebody's got to do it.«

No.  All you have to do is get a suitable message to the user
accounting for L10N/I18N.  The mechanism that you use to do this
is up to you.

I can use Literate Programming to prepare documentation in
French and English *alongside* the actual sources that I
am documenting.  Just because you don't use that technique
doesn't mean others can't!

>> When filemove changes (possibly recognizing NEW error
>> conditions or *different* error conditions), then MoveFile
>> needs to be able to explain those new "problems" for the
>> user.
>> You haven't isolated the responsibilities.
>
>    The uppermost level (= the application layer) has to explain
>    the errors to the user.

Any level that deals with any (type of) user has to explain the
errors/results to that user.  The LIBRARY has to explain its
results to the APPLICATION (using your terminology).  In my
scheme, a layer can rely on its successive underlings for more
*specific*/detailed information about the error.

>    When "filemove" is not part of the uppermost level, it does
>    not have to do this, but might just return it specific numeric
>    error codes according to its interface. But the uppermost
>    level then needs to explain what happend to the user.
>

[toc] | [prev] | [next] | [standalone]


#18186

FromKaz Kylheku <kaz@kylheku.com>
Date2012-03-04 03:20 +0000
Message-ID<20120303184551.421@kylheku.com>
In reply to#18183
On 2012-03-04, Don Y <this@isnotme.com> wrote:
> Hi Stefan,
>
> On 3/2/2012 8:05 PM, Stefan Ram wrote:
>> Don Y<this@isnotme.com>  writes:
>>> You need to maintain this "by hand".  There is nothing that
>>> prevents you from:
>>>    #define YES  (2)
>>>    #define NO   (2)
>>
>>    The include-file can be generated:
>>
>> printf( "#define YES (%d)\n", ++i );
>
> Sure!  But how do you know "YES" is ever USED anywhere?
> (replace YES with MOTOR_OVERHEATED and every other
> "error" condition that this file IMPLIES exists)
>
> Also, you have now created a dependency that "every"
> file relies upon.  Add a new error code and every file
> that #includes "errorcodes.h" has to be recompiled.

Your build system could make an exception for that file. Adding an error code
to the header isn't a change that requires anything to be recompiled which does
not refer to that code, and those files which refer to the code will get
recompiled because they have been modified to refer to that code.

A deletion followed by an addition which re-uses a previous code is
troublesome, as is any renumbering operation.

[toc] | [prev] | [next] | [standalone]


#18201

FromDon Y <this@isnotme.com>
Date2012-03-04 11:03 -0700
Message-ID<jj0aq7$sef$2@speranza.aioe.org>
In reply to#18186
Hi Kaz,

On 3/3/2012 8:20 PM, Kaz Kylheku wrote:
> On 2012-03-04, Don Y<this@isnotme.com>  wrote:
>> Hi Stefan,
>>
>> On 3/2/2012 8:05 PM, Stefan Ram wrote:
>>> Don Y<this@isnotme.com>   writes:
>>>> You need to maintain this "by hand".  There is nothing that
>>>> prevents you from:
>>>>     #define YES  (2)
>>>>     #define NO   (2)
>>>
>>>     The include-file can be generated:
>>>
>>> printf( "#define YES (%d)\n", ++i );
>>
>> Sure!  But how do you know "YES" is ever USED anywhere?
>> (replace YES with MOTOR_OVERHEATED and every other
>> "error" condition that this file IMPLIES exists)
>>
>> Also, you have now created a dependency that "every"
>> file relies upon.  Add a new error code and every file
>> that #includes "errorcodes.h" has to be recompiled.
>
> Your build system could make an exception for that file. Adding an error code
> to the header isn't a change that requires anything to be recompiled which does
> not refer to that code, and those files which refer to the code will get
> recompiled because they have been modified to refer to that code.
>
> A deletion followed by an addition which re-uses a previous code is
> troublesome, as is any renumbering operation.

Great!  Wonderful!  How are you going to address all of the *other*
criteria/criticisms I raised?

"Good news!  We managed to set your broken leg!  The cast
looks wonderful!  And, everyone on the hospital staff has
taken time out of their busy day to sign it!  Unfortunately,
the infection will (still) kill you by morning..."

[toc] | [prev] | [next] | [standalone]


#18212

FromDon Y <this@isnotme.com>
Date2012-03-04 16:31 -0700
Message-ID<jj0tvt$gg3$1@speranza.aioe.org>
In reply to#18183
Hi Stefan,

On 3/3/2012 8:02 PM, Stefan Ram wrote:
> Don Y<this@isnotme.com>  writes:
>>> The application defines the syntax it expects from its
>>> user. It then checks this syntax reporting any errors
>>> to the user.
>> You don't *really* parse the user input, keystroke by keystroke,
>> in your main(), do you??  No atof(), strtod(), sscanf(), etc.?
>
>    When I say »the application does this and that«, this
>    includes the possibility that the application might use
>    library functions to accomplish parts of »this and that«.
>
>    However, the application is in charge of what those library
>    functions do, because it calls them to do certain thinks
>    (the library functions are just »vicarious agents« or
>    »auxiliary person«, they act on behalf of the application).
>    One says that Gustave Eiffel built the Eiffel Tower, while,
>    in reality, other people took part in that effort, too.

Sure, but the application is intentionally ignoring (delegating)
the details of that activity *to* it's underling (the "library",
in your example).  The application doesn't *want* to know what
is involved in parsing a string for a particular token, etc.

The underling produces the result for the application;  *or*,
complains that it was not *able* to produce the result.

I am advocating a mechanism whereby it is easy for the underling
to embellish that "go/no-go" indication with more specific/detailed
results *and* textual descriptions that the application can pass
on to the user, if asked.

You have some (medical) test performed.  Chances are, your MD doesn't
perform it but delegates this to an agency that does it at his
request/direction.  The agency returns the results to the doctor's
office (this seems to be pretty much the rule, here, for most tests).
The doctor reviews the result, makes notes in your file and tells
<someone> (nurse?  office manager?  receptionist??) what to report
to *you*, the patient.

Usually, what you hear is a terse summary of the result:  "your
EKG was normal", "your blood sugar was elevated", "your ...".

For most/many folks, this is probably enough.  It is often accompanied
by a recommended course of action:  "cut down on your salt intake",
"get some exercise", etc.

For those of us who want more than a "summary" of the result, you
press that <someone> for more details:  "How was the EKG abnormal?",
"What was my A1c?", etc.

That <someone> often has the answer to those questions readily
available (flip to the next page on your "chart") or can get back
to you with them in short order (some offices require the doctor
to decide exactly what the patient is told).

Imagine if the agency that performed the test ONLY gave the doctor
a go/no-go result.  Or, if you were never allowed to "see" the
actual results.  How would you *understand* the doctor's (secondhand
via that <someone>) recommendations?

>    When I write a (recursive-descend) parser, usually parsing
>    char by char is more easy and feels mor safe to me. For example:
>
>    (example is provided below as appendix 1.)
>
>> There are *many* layers.  Within libraries, between libraries,
>> within subsystems, between subsystems, etc.  Any of these can
>> be designed to talk to the user.
>
>    That is tha magic of layers: They are called »layers« for a
>    reason! They are opaque! So, when you look at a layer from
>    the top, you just see that layer and not possible layers
>    below it.

Sure!  But layers have documented INTERFACES to the layer(s)
above/below.  I am advocating including this sort of information
*in* that interface.  Otherwise, you're stuck taking the word
of the agency that ran your medical test:  "Your EKG was bad"

What's your remedy?  Try changing your lifestyle in various ways
and run the test *again* -- HOPING for a "good" result?

>    In a sense, to any layer, there is always only one
>    layer: The layer /directly/ below it. It does not have to be
>    aware of the layer above it. And surely not of the layers
>    below the layer below it. So there are always just two
>    layers to consider. That is how layering reduces complexity!
>
>    Otherwise, they are not layers, but components or subsystems
>    or so.
>
>    For example, when writing TCP code, you are just concerned
>    with IP, not with the hardware layer below it.

So, if a packet can't get delivered, you don't care if the
reason is because a cable was unplugged, the network card
was MISSING, the heap ran dry, etc.?

"Please try again"

That's how users get annoyed when they do something that they
*think* is correct and are met with a message that presents
very little detail.  They try again and get the same unhelpful
message.

Should they just try random actions in the hope that they
stumble on something that DOESN'T yield an error?

I bet you'd sure like to know that the network cable was
unplugged before you start checking CIDR settings, etc.

Or, troubleshooting your code...

>    When one uses a standard file dialog, one is still
>    responsible for what the dialog does. Insofar, when an
>    application calls a file dialog, it is not forced to do so.
>    It could built a custom dialog. To the end user, it's all
>    part of just the application. The file dialog has become
>    part of the application. The application author is still
>    responsible. If the file dialog does not talk to the user as
>    it should according to the application guidelines /
>    contract, it has to be replaced by a custom dialog.

[toc] | [prev] | [next] | [standalone]


#18218

FromDon Y <this@isnotme.com>
Date2012-03-04 23:04 -0700
Message-ID<jj1l1b$snv$1@speranza.aioe.org>
In reply to#18212
Hi Stefan,

On 3/4/2012 5:25 PM, Stefan Ram wrote:
> Don Y<this@isnotme.com>  writes:
>> The underling produces the result for the application;  *or*,
>> complains that it was not *able* to produce the result.
>
>    When you say »result«, you must think of value function, i.e.,
>
> T name( parameter )
>
>    , where T is/ not/ void, so »name( args )« has a value, which is
>    the result.

No.  I mean "result" as in "product of the computation/activity".
That result might be moving an actuator, computing a value, etc.

A function (apologies to Malcolm if I am playing fast-and-loose
with terms, here) can also return a "result" to the caller.
It can do this by way of having a "type" consistent with that
result (e.g., atof() returns a "double" as its result).  Or,
it can have in/out parameters and pass its results back to
the caller that way (e.g., asciitofloat(... &result) ).  Or,
it's result can be examined by something shared that both
caller and callee can access (e.g., a global datum manipulated
by one and observed by the other).

>    In such a case, I deem the function to /always/ produce a result.
>    This might include a success and a status code, e.g.,
>
> struct result r = parseint( string );
> if( r.success )... r.value ... else ... r.status ...
>
>    So, the »parseint« function above /never/ fails.
>    It always returns a result according to its specifications.

See above.  "result" is an overloaded term.

>> I am advocating a mechanism whereby it is easy for the underling
>> to embellish that "go/no-go" indication with more specific/detailed
>> results *and* textual descriptions that the application can pass
>> on to the user, if asked.
>
>    And which natural language is used for those textual
>    descriptions?

Whatever is appropriate for the project/design team.
E.g., if I am making a product for the Spanish market, I
would want those messages to be in spanish!

>> So, if a packet can't get delivered, you don't care if the
>> reason is because a cable was unplugged, the network card
>> was MISSING, the heap ran dry, etc.?
>
>    This is not part of the TCP code, but might be handled in
>    another layer (usually in a lower layer).

Which means those lower layers communicate *other* information
(that the TCP stack chooses to ignore) to some other daemon
watching for it.  In either case, the information HAS TO
exist and has to be reported (if you ever want to know that
the cable is unplugged!  Note the goal of the mechanism I
am describing is to *increase* the amount of information
available to the user.  It's got to come from *somewhere*!)

[toc] | [prev] | [next] | [standalone]


#18134

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-03-02 22:13 -0800
Message-ID<ea871b51-f766-4da3-ab1a-169434004f0d@p7g2000yqk.googlegroups.com>
In reply to#18090
There's no good answer.

The first thing to do is to divide your code into "pure functions" and
"procedures". A pure function shuffles bits, a prodeucre shuffles bits
and does IO.
A pure function can only fail in three circumstances, it's called with
invalid parameters, there's an internal error in its coding, or it
runs out of memory.
A procedure can fail in these circumstances, and also because of a
hardware problem, because the user provides erroneous or even
malicious data, because hardware is functioning but is overwhelmed by
the demands of the prodecure, or because of missing resources such as
non-existent files.

So let's take the situations one by one. If you have invalid
parameters, that normally indicates a programming error in calling
code. The rare exception is when caller can't reasonably be expected
to check the parameters for validity - e.g. a statistical procedure
might fail when the numbers have a distribution that becomes less like
a bell curve when you take means of a sample. Caller can't reasonably
be expected to check for that condition. If caller can't be expected
to check the parameters, an "abormal" result must be passed back as
part of the normal flow control of the program. If caller can, there's
not much point in shuting the error back up to a buggy caller. You
need to abort the program with an error message if it can be aborted,
suppress the error if abortion isn't an option.

Internal error - difficult. Your own code is buggy. There's no real
answer to this situation.

Out of memory - if it can reasonably be expected that the function
will run out of memory, shunt up an "out of memory" condition to
caller. If basically this can't happen (you need to store one filename
dynmaically ona  amchine with 4GB of memory), abort with an out of
memory message.

Hardware problems: is in once in a blue moon, or can it reasonably be
expected? If it's once in a blue moon, simply report an IO error and,
usually, terminate. If it's expected, you will need to know how to
code round the expected harware failures.

Bad user data - usually you should assume that the user is a hacker
trying to make your program malfunction. Should be normal control path
of the program, and reported up to caller.

Overhelmed hardware - very difficult. If it will take a whole day to
write results to disk, is this acceptable or must be abort? It's often
not easy to answert these questions, or anticipate them. The file
might be quite small, the disk hardware very busy.

Missing resources - happens all the time. Treat as normal flow control
of program.

So generally the strategy is to treat so-called error conditions as
normal flow control, and pass the error condition up to caller. So
they're not really errors at all. The exception is errors caused by
programming mistakes. It's dangerous to pass an error back up to a
buggy caller. Normally you want to assert fail, which gives the
compiler the chice between reporting and aborting, or siliently
suppressing the error.

Centralised error systems mean that code will break if you try to move
it to a different project.

--
Basic Algorithms - massive compendioum of C programming resources
http://www.malcolmmclean.site11.com/www

[toc] | [prev] | [next] | [standalone]


#18135

FromDon Y <this@isnotme.com>
Date2012-03-03 00:49 -0700
Message-ID<jisie1$1qf$1@speranza.aioe.org>
In reply to#18134
Hi Malcolm,

On 3/2/2012 11:13 PM, Malcolm McLean wrote:
> There's no good answer.
>
> The first thing to do is to divide your code into "pure functions" and
> "procedures". A pure function shuffles bits, a prodeucre shuffles bits
> and does IO.

So, your classification treats a parser as a "pure function" -- if
the I/O has been handled outside of the scope of the parser.

> A pure function can only fail in three circumstances, it's called with
> invalid parameters, there's an internal error in its coding, or it
> runs out of memory.

In my case, a "process" (being imprecise in my terms) can also
*lose* a resource (e.g., memory) that it previously possessed.
But, its failure to act properly on the notification of that loss
would fall under the "bug" category (though there are some
degenerate cases where a resource may be withdrawn before the
process can act on the notification -- but the OS can deal with
that special case)

> A procedure can fail in these circumstances, and also because of a
> hardware problem, because the user provides erroneous or even
> malicious data, because hardware is functioning but is overwhelmed by
> the demands of the prodecure, or because of missing resources such as
> non-existent files.

How are you differentiating the parser instance from the "malicious
data" instance?  What if the data to be parsed originally came from
user I/O?  What if it came from a corrupted data store?  (is that
a "hardware failure"?)

> So let's take the situations one by one. If you have invalid
> parameters, that normally indicates a programming error in calling
> code. The rare exception is when caller can't reasonably be expected
> to check the parameters for validity - e.g. a statistical procedure
> might fail when the numbers have a distribution that becomes less like
> a bell curve when you take means of a sample. Caller can't reasonably
> be expected to check for that condition. If caller can't be expected
> to check the parameters, an "abormal" result must be passed back as
> part of the normal flow control of the program. If caller can, there's
> not much point in shuting the error back up to a buggy caller. You
> need to abort the program with an error message if it can be aborted,
> suppress the error if abortion isn't an option.

Parsing data provided (as an "input" to your pure function)
could nominally *expect* to find problems in that data through
no fault of the caller or the function operating on it.  Yet,
the intent of moving the parsing into this "pure function"
is so the caller need not be aware of the requirements the
parser imposes on the data.

I.e., this looks like "bad data/parameters" but isn't a failure
of the code from either party.  Since the parser didn't do the
I/O that originated the bad data, ...

You may not be able to communicate with the originator of that
data (e.g., if you read it from a file ... or, a const array
embedded somewhere in your image).  And, you may not want to abort
the "program" -- just that *aspect* of the processing.

> Internal error - difficult. Your own code is buggy. There's no real
> answer to this situation.

It depends on the nature of your error (as do hardware errors!).
Your code might still be able to reliably report an error.  It
just might not make sense to you -- until you examine your
code and see why your code is "deliberately" making that mistake.

I.e., if the results aren't safety critical, you make a best
effort and hope for the best.  (I have some systems where
"/* CAN'T HAPPEN */" really should NEVER happen and, as a
result, you want to lock up the processor *hard* so that
it doesn't do anything potentially dangerous or lossy)

> Out of memory - if it can reasonably be expected that the function
> will run out of memory, shunt up an "out of memory" condition to
> caller. If basically this can't happen (you need to store one filename
> dynmaically ona  amchine with 4GB of memory), abort with an out of
> memory message.

But the user doesn't want to see "out of memory".  The user doesn;t
need to *know* what's going on inside the code.  The memory error
needs to be reinterpreted in the context of higher levels in the
application and expressed in a way that is appropriate to that
application and user base.  This allows lower level libraries to
be reused between applications and *within* an application.

Running out of memory when trying to add a name to an address
book should yield a different message than when trying to print
a banner.  The role of the memory allocation -- along with its
persistence and remedies -- varies based on that context.

I.e., "Your address book is full.  Delete one or more entries
of you want to add this new contact" vs. "Image too large to
print.  Try a lower resolution or size."  Putting the "out of
memory" error into context adds value to the user (and thus
the product).

> Hardware problems: is in once in a blue moon, or can it reasonably be
> expected? If it's once in a blue moon, simply report an IO error and,
> usually, terminate. If it's expected, you will need to know how to
> code round the expected harware failures.
>
> Bad user data - usually you should assume that the user is a hacker
> trying to make your program malfunction. Should be normal control path
> of the program, and reported up to caller.

[Again, how does this tie in with the parser "pure function"?]

IMO, you want to provide as much information to the user to enable
them to correct their entry (even if it is a malicious user).  This
is where exposing internal "errors" from a lower level function
(e.g., parser) can benefit the user without burdening the application
or tying low level routines to a particular application.

For example, complaining that an integer value contained a decimal
point can be reported to the user regardless of whether the upper
layer that invoked the parsing routine was using it to fetch an
*age*, IQ, ZIP code, etc.  The context associated with the upper
layer gives the "problem report" ("error" is a hard word to
avoid) some meaning; further qualification obtained from the
report of the lower level parser helps to clarify that:

Upper context: "The age that you entered is invalid."
Lower context: "The value must be a whole number -- it can not
    contain a decimal point"

> Overhelmed hardware - very difficult. If it will take a whole day to
> write results to disk, is this acceptable or must be abort? It's often
> not easy to answert these questions, or anticipate them. The file
> might be quite small, the disk hardware very busy.
>
> Missing resources - happens all the time. Treat as normal flow control
> of program.
>
> So generally the strategy is to treat so-called error conditions as
> normal flow control, and pass the error condition up to caller. So
> they're not really errors at all. The exception is errors caused by
> programming mistakes. It's dangerous to pass an error back up to a
> buggy caller. Normally you want to assert fail, which gives the
> compiler the chice between reporting and aborting, or siliently
> suppressing the error.

If the assertion is going to just terminate the program, then
the user has learned nothing.  He reruns the program and it
dies, again.

> Centralised error systems mean that code will break if you try to move
> it to a different project.

I don't see that as a consequence of this sort of approach.
My scheme pieces errors (and descriptions) together as they
are encountered in the executable.  Out_Of_Memory might resolve
to 0x2345678 today, 0x107 tomorrow and NEVER HAPPEN some time
next week.  (i.e., grep the sources and you never FIND that
string in it!)

Furthermore, *your* instance of the sources might have a
different set of errors (and, thus, codes) than *my* image
based on the types of errors that our respective assignments
require us to detect/encounter.

And, when our images are merged, we magically end up with the
union of error messages and "codes" that we have individually
used.

As I said, I already have a version of this working -- borrowing
algorithms from my IDL compiler (same issues -- automatically
managing "message types").  But, it is hard to tell make(1)
when the error codes might have changed.  So, any change to
a source file requires rebuilding the error code catalog.
The only way I can currently get make to cooperate is if I
introduce an intermediate file that I only touch(1) if the
error codes need to be updated.  Then, put a separate dependency
on that file that triggers the building of the error codes
for other files.  As projects get bigger, this approach doesn't
scale well  :<  (or, it coerces you into doing things in
less than optimal ways *just* to workaround the tools)

[I also have a problem using __FILE__, __func__ and __LINE__ as
components of unique identifiers as I might have 20 files named
get.c.  Each with a function called get() within.  And the
structure of each get() highly resembling the other 19 get()'s
(i.e., signaling errors on the same __LINE__s though physically
different files).  And, I place strong restrictions on what can
be done with these "error codes" -- they aren't "regular numbers"]

If you try to hard-code error "codes", then you ARE stuck with a
system that only works for *you* and *your* application.  Not a
very smart move, IMO.

Barring some other clever approach, I will just write a utility to
build the error codes and add it into the build process -- using
the IDL compiler as a rough framework (same sorts of issues).  I
don't want to head down that path until I know that's the *best*
option!

[toc] | [prev] | [next] | [standalone]


#18139

FromMalcolm McLean <malcolm.mclean5@btinternet.com>
Date2012-03-03 03:32 -0800
Message-ID<c58608b7-d3cf-4b1b-afb6-30f5fab77bcc@gr6g2000vbb.googlegroups.com>
In reply to#18135
On Mar 3, 7:49 am, Don Y <t...@isnotme.com> wrote:
> Hi Malcolm,
>
> On 3/2/2012 11:13 PM, Malcolm McLean wrote:
>
> > There's no good answer.
>
> > The first thing to do is to divide your code into "pure functions" and
> > "procedures". A pure function shuffles bits, a prodeucre shuffles bits
> > and does IO.
>
> So, your classification treats a parser as a "pure function" -- if
> the I/O has been handled outside of the scope of the parser.
>
A parser is a pure function (assuming IO is not handled by the
function). It takes one sequence of bits - the script - and it returns
a another sequence of bits - the result. So all it's doing is
shuffling bits about in memory. If a pure function calls a procedure,
it's no longer a pure function.

However most parsers will do IO. That leads to a question. If a pure
function takes a function pointer as a parameter, and that parameter
is to a procedure, is it still a pure function? The answer is no, but
here the classification is beginning to lose its usefulness.

Parsers are an obvious case of invalid input not being reasonably
caught by caller. You could require that caller pass only correct
scripts to the parser, but that means caller has to do almost as much
work as the parser itself to verify that the script is correct. So you
have to treat malformed scripts as part of the normal flow control of
the parser. The caler's then got the problem. If the script was typed
into the source byt he programer, it's an internal error. If the
script was provided by outside, it should usually be treated as normal
flow control - you expect complex user-provided scripts to be
malformed.

[toc] | [prev] | [next] | [standalone]


#18153

FromDon Y <this@isnotme.com>
Date2012-03-03 12:04 -0700
Message-ID<jitq0d$2fq$1@speranza.aioe.org>
In reply to#18139
Hi Malcolm,

On 3/3/2012 4:32 AM, Malcolm McLean wrote:
> On Mar 3, 7:49 am, Don Y<t...@isnotme.com>  wrote:
>> On 3/2/2012 11:13 PM, Malcolm McLean wrote:

>>> The first thing to do is to divide your code into "pure functions" and
>>> "procedures". A pure function shuffles bits, a prodeucre shuffles bits
>>> and does IO.
>>
>> So, your classification treats a parser as a "pure function" -- if
>> the I/O has been handled outside of the scope of the parser.
>
> A parser is a pure function (assuming IO is not handled by the
> function). It takes one sequence of bits - the script - and it returns
> a another sequence of bits - the result. So all it's doing is
> shuffling bits about in memory. If a pure function calls a procedure,
> it's no longer a pure function.

Understood.

> However most parsers will do IO. That leads to a question. If a pure
> function takes a function pointer as a parameter, and that parameter
> is to a procedure, is it still a pure function? The answer is no, but
> here the classification is beginning to lose its usefulness.
>
> Parsers are an obvious case of invalid input not being reasonably
> caught by caller. You could require that caller pass only correct
> scripts to the parser, but that means caller has to do almost as much
> work as the parser itself to verify that the script is correct. So you

That was the point I was trying to bring out.  Do you call the
"parameter" passed to it a "programming error"?  That would imply
the caller had the responsibility of checking it for correctness
(eliminating the need for the parser's functionality).

> have to treat malformed scripts as part of the normal flow control of
> the parser. The caler's then got the problem. If the script was typed
> into the source byt he programer, it's an internal error. If the
> script was provided by outside, it should usually be treated as normal
> flow control - you expect complex user-provided scripts to be
> malformed.

I think "expect" is overly cynical.  I would say "you PREPARE FOR
<any input> to be 'malformed'" -- since you can't control the
outside world.

Returning to my original example (scanning a string of chars to
extract the numeric value expected to be represented therein),
that function (pure function using your terminology) can report
the nature of any "incompatible input" to any degree of detail
that it considers appropriate.  Not just "hey, I couldn't find
a valid 'number' in this mess..."

Those reports make sense in the context of a function that
is responsible for extracting the numeric value represented by a
string of characters.  REGARDLESS OF WHAT THAT NUMBER IS BEING
USED FOR.  And, regardless of the actual value!

E.g., if that routine also had the responsibility of testing that
the value was within a particular set of bounds, it could add
"Value specified exceeds the maximum allowed.", etc. to its
lexicon of errors.

None of these reports need to know whether the "value" is being
used as a representation of AGE, azimuth/elevation, speed,
stock market index, etc.  You are dealing with a *value* -- just
like when you complain that the first letter of a sentence is
not capitalized (you don't even look at the CONTENT of the
sentence to understand the nature of that "report").

But, you obviously don't want that "pure function" to become
a "procedure" by virtue of informing the user directly of
this error (this would also badly contort the design of
your code).

Nor do you need to return the text of the message to the caller!
(how do you decide whether you should return the en_US version
of the message or the es version?)

Instead, return an identifier that allows the caller to fetch
a version of the error that *it* considers appropriate.

It, in turn, can embellish -- or replace -- that message with
something appropriate to its understanding of the application.
E.g., perhaps the caller is responsible for obtaining the
coordinates of a point in 2D space.  It *uses* the called routine
to parse the string (that is provided to *it* by ITS caller!)
for the X coordinate, then the Y coordinate.  An error reported
during the first parse might cause it to report an error to
*it's* caller like:  "Bad value for X coordinate." AUGMENTED
by the report returned by the function that had actually
detected the lower level error "Non-numeric character encountered
in value."

Or not (depends on the range of errors that you document
get_point() as reporting).  You could, instead, ignore the
detail presented to you by any of your "underlings" and
just report:  "Bad point".

[toc] | [prev] | [next] | [standalone]


#18158

FromLowell Gilbert <lgusenet@be-well.ilk.org>
Date2012-03-03 15:06 -0500
Message-ID<44obsd8n5d.fsf@lowell-desk.lan>
In reply to#18153
Don Y <this@isnotme.com> writes:

> Returning to my original example (scanning a string of chars to
> extract the numeric value expected to be represented therein),
> that function (pure function using your terminology) can report
> the nature of any "incompatible input" to any degree of detail
> that it considers appropriate.  Not just "hey, I couldn't find
> a valid 'number' in this mess..."
>
> Those reports make sense in the context of a function that
> is responsible for extracting the numeric value represented by a
> string of characters.  REGARDLESS OF WHAT THAT NUMBER IS BEING
> USED FOR.  And, regardless of the actual value!
>
> E.g., if that routine also had the responsibility of testing that
> the value was within a particular set of bounds, it could add
> "Value specified exceeds the maximum allowed.", etc. to its
> lexicon of errors.
>
> None of these reports need to know whether the "value" is being
> used as a representation of AGE, azimuth/elevation, speed,
> stock market index, etc.  You are dealing with a *value* -- just
> like when you complain that the first letter of a sentence is
> not capitalized (you don't even look at the CONTENT of the
> sentence to understand the nature of that "report").
>
> But, you obviously don't want that "pure function" to become
> a "procedure" by virtue of informing the user directly of
> this error (this would also badly contort the design of
> your code).
>
> Nor do you need to return the text of the message to the caller!
> (how do you decide whether you should return the en_US version
> of the message or the es version?)
>
> Instead, return an identifier that allows the caller to fetch
> a version of the error that *it* considers appropriate.
>
> It, in turn, can embellish -- or replace -- that message with
> something appropriate to its understanding of the application.
> E.g., perhaps the caller is responsible for obtaining the
> coordinates of a point in 2D space.  It *uses* the called routine
> to parse the string (that is provided to *it* by ITS caller!)
> for the X coordinate, then the Y coordinate.  An error reported
> during the first parse might cause it to report an error to
> *it's* caller like:  "Bad value for X coordinate." AUGMENTED
> by the report returned by the function that had actually
> detected the lower level error "Non-numeric character encountered
> in value."
>
> Or not (depends on the range of errors that you document
> get_point() as reporting).  You could, instead, ignore the
> detail presented to you by any of your "underlings" and
> just report:  "Bad point".

The distinction you're making here is about providing appropriate
abstraction. Your technique of ginning up an error value from the file
name and line number doesn't help with this, because the agreement on
symantic returns is between caller and callee -- going further back the
call chain isn't helpful (in fact, it's actively harmful) to maintaining
usable abstrations.

Now that you're thinking in terms of abstractions, it also becomes less
important to worry about exactly what the "identifier" is, and you can
just focus on what it means. It's not a number, it's an object, which
represents everything the caller needs to know about the error. It can
attach additional information, or translate into a different convention
for representing errors, before it reports back to *its* caller.

You'd like a system for adding more information about the error, so each
level can use the specificity of the lower level, and add on the
abstraction that it was hiding from the higher level. This is a
fundamentally different (and vastly more interesting) topic than what
the bottom-level caller reports to its immediate caller.

Be well.
-- 
Lowell Gilbert, embedded/networking software engineer
	 http://be-well.ilk.org/~lowell/

[toc] | [prev] | [next] | [standalone]


#18167

FromDon Y <this@isnotme.com>
Date2012-03-03 14:38 -0700
Message-ID<jiu302$q8p$1@speranza.aioe.org>
In reply to#18158
Hi Lowell,

On 3/3/2012 1:06 PM, Lowell Gilbert wrote:
> Don Y<this@isnotme.com>  writes:

[much elided]

>> None of these reports need to know whether the "value" is being
>> used as a representation of AGE, azimuth/elevation, speed,
>> stock market index, etc.  You are dealing with a *value* -- just
>> like when you complain that the first letter of a sentence is
>> not capitalized (you don't even look at the CONTENT of the
>> sentence to understand the nature of that "report").
>>
>> But, you obviously don't want that "pure function" to become
>> a "procedure" by virtue of informing the user directly of
>> this error (this would also badly contort the design of
>> your code).
>>
>> Nor do you need to return the text of the message to the caller!
>> (how do you decide whether you should return the en_US version
>> of the message or the es version?)
>>
>> Instead, return an identifier that allows the caller to fetch
>> a version of the error that *it* considers appropriate.
>>
>> It, in turn, can embellish -- or replace -- that message with
>> something appropriate to its understanding of the application.
>> E.g., perhaps the caller is responsible for obtaining the
>> coordinates of a point in 2D space.  It *uses* the called routine
>> to parse the string (that is provided to *it* by ITS caller!)
>> for the X coordinate, then the Y coordinate.  An error reported
>> during the first parse might cause it to report an error to
>> *it's* caller like:  "Bad value for X coordinate." AUGMENTED
>> by the report returned by the function that had actually
>> detected the lower level error "Non-numeric character encountered
>> in value."
>>
>> Or not (depends on the range of errors that you document
>> get_point() as reporting).  You could, instead, ignore the
>> detail presented to you by any of your "underlings" and
>> just report:  "Bad point".
>
> The distinction you're making here is about providing appropriate
> abstraction.

Yes.  At any given level, the extent to which you can "generalize"
or "specialize" the information that you report varies.  You
interpret an "error" (it's really hard to avoid using that term
though "report" seems too generic) in the context that *you* are
aware of.

"What is the nature of this activity that I have delegated to
this underlying routine -- GIVEN MY ROLE IN THE UNIVERSE?"

> Your technique of ginning up an error value from the file
> name and line number doesn't help with this, because the agreement on

The file/line/function is only used to create a KNOWN TO BE UNIQUE
identifier for this INSTANCE of a particular error.  E.g., you might
check for a particular "requirement" in several different (disjoint)
places in a function.  They might all represent the same general
error (e.g., out of memory) that you will report.

But, they originated in different places!  One was raised as
the result of conditional_1 while another might be from
conditional_3.  By reporting *where* the error was raised,
someone "remotely" can see exactly where IN THE CODE the
system is reporting the error.  This helps cover the case
of buggy code:

"Your software is complaining that I have 'too many files
open'.  When pressed for details, it tells me that there
is a limit of '9' files.  Well, I only have *7* files open!"

"Could you please tell me the 8 digit error code displayed
in the lower left corner?"

"Sure.  10330449."

"Hmmm... ah, OK.  Yes, I see.  That error stems from the
fact that the code WHERE IT IS RAISED says:
     if (num_files > 7) {
instead of:
     if (num_files > MAX_FILES) {
Thanks for bringing this to our attention!"

(Of course, that last comment would not be conveyed to the
user in that level of detail.  Instead, you'd say, "I'll
submit a report of this problem and we'll have an update
available for you _______")

> symantic returns is between caller and callee -- going further back the
> call chain isn't helpful (in fact, it's actively harmful) to maintaining
> usable abstrations.
>
> Now that you're thinking in terms of abstractions, it also becomes less
> important to worry about exactly what the "identifier" is, and you can
> just focus on what it means. It's not a number, it's an object, which
> represents everything the caller needs to know about the error.

Exactly.  From the *caller's* viewpoint, it is a "bad area code"
or "bad IQ value", etc.  It doesn't need to be concerned with
*what* makes it "bad".

OTOH, if the user is confused or DISAGREES with that assessment, you
want a means for getting that information to the user.  But *you*
(the caller) don't want to have to independently examine the
issue (duplicating the efforts of the callee -- possibly in a
DIFFERENT manner!).  So, you want to be able to expose the next
level of detail to the user... "drill down".

The new information will be more general (in terms of abstraction
since it knows nothing about the meaning of the "bad value")
and more SPECIFIC in terms of the problem that it is reporting:
"non decimal digit", "too many decimal points", etc.

> It can
> attach additional information, or translate into a different convention
> for representing errors, before it reports back to *its* caller.

Exactly.

> You'd like a system for adding more information about the error, so each
> level can use the specificity of the lower level, and add on the
> abstraction that it was hiding from the higher level. This is a
> fundamentally different (and vastly more interesting) topic than what
> the bottom-level caller reports to its immediate caller.

Yes.  Ideally, the UI level first reports to the "user"
(whatever that "user" might be).  In a spreadsheet application,
the way that a particular error (<frown>  again, bad word) is
reported would make sense to a "spreadsheet user".

 From the spreadsheets point of view, a more detailed description
of the error makes sense.

E.g., if OpenFile() fails because of Err_NoMemory, the application
might free up some memory and repeat the operation.  OTOH, if
Err_NotFound is returned, it might just report that to the user.
The code only cares about the "text" of the error when it needs
to report that to an organic being.

The "error CODES" are just symbolic ways for the software to
*efficiently* understand the results reported.  The software
needs to be able to efficiently decide if this is an "error",
"warning" (potential "qualification") or successful result.
It doesn't want to understand English, French, Portuguese, etc.
to garner that information.

[toc] | [prev] | [next] | [standalone]


Page 3 of 5 — ← Prev page 1 2 [3] 4 5  Next page →

Back to top | Article view | comp.lang.c


csiph-web