Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #18090 > unrolled thread
| Started by | Don Y <this@isnotme.com> |
|---|---|
| First post | 2012-03-01 12:48 -0700 |
| Last post | 2012-03-06 11:21 -0700 |
| Articles | 20 on this page of 87 — 17 participants |
Back to article view | Back to comp.lang.c
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 →
| From | Phil Carmody <thefatphil_demunged@yahoo.co.uk> |
|---|---|
| Date | 2012-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]
| From | Kaz Kylheku <kaz@kylheku.com> |
|---|---|
| Date | 2012-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]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2012-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]
| From | Jonathan Leffler <jleffler@earthlink.net> |
|---|---|
| Date | 2012-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]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-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]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2012-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]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-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]
| From | "BartC" <bc@freeuk.com> |
|---|---|
| Date | 2012-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]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Kaz Kylheku <kaz@kylheku.com> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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]
| From | Lowell Gilbert <lgusenet@be-well.ilk.org> |
|---|---|
| Date | 2012-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]
| From | Don Y <this@isnotme.com> |
|---|---|
| Date | 2012-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