slightly OT: error handling conventions

J

Jirka Klaue

Richard said:
Jirka said:
Richard Heathfield wrote:

IMO this method has two drawbacks. It leads to functions doing
little with *many* lines.

Too many lines? That's a drawback, huh? Presumably you have to pay extra for
newlines. I must admit I had no idea it was a problem. Okay, watch this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "commsi.h"
int TCPGetMessage(TCP*connection,char**Message,size_t*MaxSize,const [many lines snipped]

So that's the line problem sorted.

Do you think so? And why did you remove all the other white space too?
I was not discussing formatting or indentation. I said, I prefer not
to write unnecessary lines (code), if lesser lines (code) suffice.
That has not been my experience. YMMV.
Obviously.


I don't believe so. If you think differently, please feel free to provide a
reference.

Okay, watch this. :) <[email protected]>

Jirka
 
R

rihad

I've learned in my first days of programming (the dark age where not
even assembly was available) that

- goto is evil and should be avoided whenever possible
(o.k., in very low language as mashine code
or assembly it is truly impossible but for that
you can always design the code so that there is no
long goto.
And with C you can design the code always in a
manner that NO goto is needed...

.... by writing code that emulates gotos. Of course you can get rid of gotos, by
introducing a level of abstraction over gotos (like extra logic etc etc). I
really don't understand you people, those who say that gotos are evil, but I'm
trying real hard :)
Good design in programming level is:

- a fuction exeeding substantial more than one screen page
is buggy per design.

This is hilarious. Are we talking about 80x25 terminal emulation screen or
1600x1200 gvim in a maximized window? :)
Seriously, if what a function does can be considered atomic, if there are no
smaller useful subactions that could warrant making it out of that function into
functions of their own, then it can get as long as it gets. It doesn't happen
*usually*, but the function *may* get as long as it gets.
- define macros you creates only for increasing the readability of
code near the place(s) you need them. Don't forged to #undef them.

Agreed.
 
D

Default User

Richard said:
I have some N-dimensional paper for sale. Only US$1000000 per dimension per
sheet.


Are you charging the same for "rolled up" dimensions?



Brian Rodenborn
 
D

Dan Pop

In said:
Oh really? You've been programming since before assembly
was available? When was that then, 1948?

The availability of an assembler is strongly connected to the availability
of a hosted system capable of running it. As late as 1983, I had to
develop embedded control applications without the help of an assembler,
because the *only* computer available was the one I was programming for
and its resources (1K EPROM, 64 bytes RAM) were not enough for running
an assembler on.

By 1984 our department finally got a computer (a PDP-11 clone) and I
could write and use an assembler for the microcontroller in question.

Dan
 
I

Ian Woods

Ian Woods wrote:

8> For code where I have multiple resources to find, use, then release
I

My question is this: Have you /considered/ other possibilities,
especially w.r.t. maintenance issues, or are you content with the goto
in this context? That is, have you explored this runlevel idea, /sans/
goto? If so, what were your findings?

Using goto for this isn't the 'best' solution for most people, especially
with regards to maintenance. There are three considerations when making
code, or, like this is a code template:

o ease of writing
o ease of reading
o ease of maintenance

The goto approach is not the best at any of these, but it is easy to
write, easy to read /and/ easy to maintain.

It eventually grew out of the application of some of the techniques seen
elsethread. It is fact exactly equivalent to the nested-if technique you
posted. The big problem I have with that technique is that I find it
difficult to keep track of which allocation code is associated with which
cleanup code. When I have used that technique and had to revise the code,
I made the error of putting the 'right' code in the wrong place too often
for me to be comfortable.

The reason why it's goto which is used as opposed to something like break
in a do-loop is because of the kind of problems I'm typically solving.
It's common that I need to handle an error condition several loops in.
Either I need to write magic code to exit each loop, or I use goto. The
latter has some interesting uses for cleanup of the effects of nested
loops:

int foo(size_t w) {
int rl=0;
int rv=ERROR:

struct Object ** arr=NULL;
size_t x=0;

arr=malloc(w*sizeof *arr);
if (!arr) goto on_err;

for (x=0;x<w;x++) {
arr[x]=Object_Alloc();
if (!arr[x]) goto on_err;
}
rl++;

/* rl=1 - do stuff*/
rv=OKAY;

on_err:
switch(rl) {
case 1: x=w; /* so we can reuse x in rl 1 and higher... */
case 0: /* cleanup of arr */ {
size_t i;

for (i=0;i<x;i++) {
Object_DeAlloc(arr);
}
free(arr);
}
}

return rv;
}

The goto is a one-size-fits-all solution to passing control to the
cleanup code. No additional code is needed to get to the cleanup stage as
with other things like breaking from within nested loops.

Summary: yes, I have considered other options, and the goto one is the
one I like 'best'. :)
[Personal aside: Ian, I haven't forgotten, honest! I hope to get my
email fixed up any month now.]

Don't worry - I've been trying hard so that the senior management don't
close my project (and funding!). Keeping them happy has meant that I've
had little time for sleep, never mind anything more interesting.

Ian Woods
 
R

Richard Heathfield

Jirka said:
Richard said:
Jirka said:
Richard Heathfield wrote:

IMO this method has two drawbacks. It leads to functions doing
little with *many* lines.

Too many lines? That's a drawback, huh? Presumably you have to pay extra
for newlines. I must admit I had no idea it was a problem. Okay, watch
this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "commsi.h"
int TCPGetMessage(TCP*connection,char**Message,size_t*MaxSize,const [many lines snipped]

So that's the line problem sorted.

Do you think so?

No. But I didn't think there was a line problem to start with.
And why did you remove all the other white space too?

To reduce the number of lines. ;-)
I was not discussing formatting or indentation. I said, I prefer not
to write unnecessary lines (code), if lesser lines (code) suffice.

I got 64 lines down to 17. I think that counts as fewer lines. %-)
Obviously.

Well, in all seriousness, I use a terminal window that can easily display 50
lines in perfect clarity. You can see a fair bit of code in 50 lines, even
with extra braces and vertical whitespace and so on. I am currently looking
at the unmunged original of the code I posted in my previous article. I can
see almost the entire function on a single screen. If I used a slightly
smaller font, I could get the whole thing. I don't see a problem here. Yes,
if I used a terser style, I could see two functions at the same time. But I
don't want, or need, to see two functions at the same time!

LOL! Well, that was only a promise to *myself*. I'm always breaking those.
 
R

Richard Heathfield

Ian said:
Using goto for this isn't the 'best' solution for most people, especially
with regards to maintenance. There are three considerations when making
code, or, like this is a code template:

o ease of writing
o ease of reading
o ease of maintenance

The goto approach is not the best at any of these, but it is easy to
write, easy to read /and/ easy to maintain.

Thanks for explaining. BTW ISTR proposing something along these lines whilst
working for an airline company about 7 or 8 years ago, and it was laughed
out of court, because "every other statement will be a goto! You have /got/
to be kidding!" and, of course, I wasn't really all that keen on the idea
myself, so I didn't pursue it. Mind you, I hadn't thought of your runlevel
idea; it might just have sold the package to my team.
It eventually grew out of the application of some of the techniques seen
elsethread. It is fact exactly equivalent to the nested-if technique you
posted. The big problem I have with that technique is that I find it
difficult to keep track of which allocation code is associated with which
cleanup code.

That's interesting, because I find it quite intuitive, and I suppose I just
assumed that other people do too.
Summary: yes, I have considered other options, and the goto one is the
one I like 'best'. :)

Or dislike least, perhaps?
 
C

Christopher Benson-Manica

Richard Heathfield said:
if(line[0] == '#') /* comment - skip it */
{
continue;
}
nevertheless I tend to use continue only to self-document empty loops in an
uncompromising manner (the lack of loop contents could be said to be
self-documenting for an empty loop, but it could also be read as
"programmer forgot loop contents"!).

I'm curious, how do you resolve situations such as the one you presented
without using continue? It seems to me that the main alternative is to just
wrap everything in if{} else if{} ..., which seems substantially less clean
than the code above. Same thing goes for break - perhaps I rely too much on
its convenience, but to me it is very convenient indeed...
 
M

Micah Cowan

rihad said:
Not *exactly*. ((void *) 0) is a common way to represent 0 - the null pointer
constant - behind the NULL macro.

No. 0 is *a* null pointer constant; (void*)0 is the *other* null
pointer constant. Reread 6.3.2.3#3.
 
T

The Real OS/2 Guy

... by writing code that emulates gotos. Of course you can get rid of gotos, by
introducing a level of abstraction over gotos (like extra logic etc etc). I
really don't understand you people, those who say that gotos are evil, but I'm
trying real hard :)

No emulation of goto is needed when the design is clean and fine.

Startup to design the code in the manner
- describe what is to do. When it gets more complex bereak it up in
lower levels
- then when all what is defined make the HOW is it done.

make thinks simple, make it more simple if it is complicate. The
result will be significant less error and significant less debug time.
And a side effect is you would not write a single got - because its
not needed.

This is hilarious. Are we talking about 80x25 terminal emulation screen or
1600x1200 gvim in a maximized window? :)

The time where a screen was only 25 lines long is gone more than 10
years ago. At that time we had functions with about 2 screens in
length. With a screen resolution of 1280x1024 you get more than 80
lines and even with 1024x768 you gets more than 50 lines.

That rule is really old - it comes from the time whereas a tty was a
modern console and a line printer the standard print device. So 50
lines (is the net number of lines of a page of paper. So you had a
block of pages of paper to study until you got another time on a
terminal. It was easier to have a function on one side - and now
whereas you doesn't needs to print anything you have a screen that is
good enough to hold even such page in once.
Seriously, if what a function does can be considered atomic, if there are no
smaller useful subactions that could warrant making it out of that function into
functions of their own, then it can get as long as it gets. It doesn't happen
*usually*, but the function *may* get as long as it gets.

A fuction that contains multiple nedsted loops is surely not atomic.
It is quickly on a point whereas you does more work on too much
details at once. Breaking that up to what is done - and that in how
get its done makes it not only more readable but makes debugging that
more easy. E.g. quick step over instead of fiddling around with
breakpoints when you knows that this detail does as it should.

Write goto and you goes searching the continous point in the same
logic level - and the destination of a goto can be somewhere. Write a
return and you reads it as 'this level of work is done. Continue on
the next higher level. In debug you can even quick step over the
fucntion without seeing the detail.
 
R

Richard Heathfield

Christopher said:
Richard Heathfield said:
if(line[0] == '#') /* comment - skip it */
{
continue;
}
nevertheless I tend to use continue only to self-document empty loops in
an uncompromising manner (the lack of loop contents could be said to be
self-documenting for an empty loop, but it could also be read as
"programmer forgot loop contents"!).

I'm curious, how do you resolve situations such as the one you presented
without using continue?

if(line[0] != '#') /* comment - skip it */
{
rc = ProcessLine(line);
}
It seems to me that the main alternative is to
just wrap everything in if{} else if{} ..., which seems substantially less
clean
than the code above.

My alternative looks pretty clean to me. :)
Same thing goes for break - perhaps I rely too much
on its convenience, but to me it is very convenient indeed...

I don't dispute the convenience of break and continue to the writer of the
code. It is their inconvenience to the /reader/ that gives me pause.
 
C

CBFalconer

The said:
.... snip ...

Even as I learned that usage is global variables is evil I won a
fight in using them instead of pup them through endless levels as
parameters.

It was a question of performance! Pumping many parameters through
deep levels of functions costs significantly more time than simply
use the values as global - and as runtime was the most critical
point globals were the solution.

What you really want is the variable visibility and restrictions
found by using nested procedures, as in Pascal. The best
approximation of this you can get in C involves using separate
files and static file scope variables. This has the disadvantage
that things that should really be automatic aren't, so that their
memory is not released when not needed, and the simultaneous
advantage that routines and variables can be global to multiple
functions.

At the same time such usage makes the routines non-reentrant and
non-thread-safe, due to the lack of automatic storage for the
variables.
 
C

Christian Bau

"The Real OS/2 Guy said:
Goto IS evil as it destroys the control flow. There is not a single
cause where goto may useful. Make a good design of your program, your
functions and you will not even come of the idea that you may need a
goto.

Does this need any comment? I don't think so.
 
S

Sheldon Simms

Does this need any comment? I don't think so.

I hesitate to get too personal, but I just have to ask.

Were you kicked out of Germany because of your pro-goto opinions
Christian?
 
I

Ian Woods

That's interesting, because I find it quite intuitive, and I suppose I
just assumed that other people do too.

I can easilly handle it for the first few ifs. The problem is that after
4 or so, I start to lose track and am quite likely to put the right code
at the wrong indentation level when update the code.

It was when I was allocating a dozen or so non-trivial 'things' that I
started exploring new ways of doing this. What I came up with originally
is 'less offensive' in that it doesn't use goto's, but it takes more
effort to write and only obviously applies to OO style objects in C:

struct Foo {
int rl;
ObjType1 * one;
ObjType2 * two;
...
ObjType12 * twelve;
};

struct Foo * FooAlloc() {
struct Foo * f;
int status;

status=FooAllocInner(&f);
if (!status) {
FooDealloc(f);
f=NULL;
}

return f;
}

int FooAllocInner(struct Foo ** f) {
*f=NULL;

*f=malloc(sizeof **f);
if (!*f) return 0;
*f->rl=0;

*f->one=OneAlloc();
if (!*f->one) return 0;
*f->rl++;

...

*f->one=TwelveAlloc();
if (!*f->twelve) return 0;
*f->rl++;

return 1;
}

void FooDealloc(struct Foo * f) {
if (!f) return;
switch(f->rl) {
case 12: TwelveDealloc(f->twelve);
...
case 1: OneDealloc(f->one);
case 0: free(f);
}
}

The goto based version isn't as neat as this, but it is much more
applicable.
Or dislike least, perhaps?

It's the best approach I've found so far out of all the ones I've tried.
I don't have any strong dislikes about it, except for the 'verbiage' of
repeated "if (!x) goto_err; else rl++;" lines. Judicious use of a macro
eliminates that almost painlessly, so I don't have any real complaints...

Well, apart from the use of goto which I occasionally get frowned at for.
:)

Out of all this discussion though, I've actually worked out a good rule
for when to use goto! Use goto when you need control-flow your
programming language doesn't natively support.

That's what the 'runlevel' example of mine does, and may be why it isn't
completely offensive to everyone (including me!).

Ian Woods
 
T

The Real OS/2 Guy

What you really want is the variable visibility and restrictions
found by using nested procedures, as in Pascal. The best
approximation of this you can get in C involves using separate
files and static file scope variables. This has the disadvantage
that things that should really be automatic aren't, so that their
memory is not released when not needed, and the simultaneous
advantage that routines and variables can be global to multiple
functions.

Won't help really. The project was an OS for a special kind of
processor family.
About 4,000,000 lines of code in about 400 modules. And a team of 40
developers working together.

Enty gate: user API calls comes here in
IRQ gate : IRQ comes her in and gets dispatched to its real sourse
This was done because an IRQ was commony coming from an
multiple cascaded hardware interface (256 * 256 * n)
devices on one.

So each of the entry points had either to call some functions with 2
to 200 parameters or to put anything on static places and make the
call parameterless. Parameterless calls are cheap in runtime, up tu 4
parameters are normal and more parameters were really expensive in a
call. So saving parameters was the main point to get runtime save
functionality.

There were 4 differen address spaces to handle:
- user addressroom data
- user addressroom code
- OS internal addressroom data
- OS internal addressroom code

Each addressroom was limited to 64 KB. Swappig addressroom was really
expensive on CPU, so the call cate had to collect the parameters from
user addressroom, store them inside the OS addressroom (including data
coming in as pointers, but excluding disk and other media buffers) to
save any driver and fuction to catch them theyrself.

But as that there were some experienced programmers who were crying
that global data is evil. Yes global data IS evil - but inside an OS
performance is more relevant than avoiding globals. Breaking code in
deep levels of functions, breaking it up in different translation
units is standard - even inside an OS. Has nothing to do with pascal -
except that pascal is unable to translate multiple soursec seapartele
and bind statically with assembler modules.

At the same time such usage makes the routines non-reentrant and
non-thread-safe, due to the lack of automatic storage for the
variables.


Threadsave is impossible when you have exactly one interrupt level you
runs in and this is the highest below NMI and hardware IRQ level. So a
call from a user runs exclusive on the CPU until it comes back to the
user gate that gives the CPU back to a user function - except an NMI
or hardware IRQ occures. So saving CPU time is most critical in an
realtime OS to let all CPU ever possible the user.
 
D

Dan Pop

In said:
No. 0 is *a* null pointer constant; (void*)0 is the *other* null
pointer constant. Reread 6.3.2.3#3.

What about all the other null pointer constants, like 0L, (1 - 1L),
(void *)(sizeof(int) - sizeof(unsigned)) and so on? ;-)

Dan
 
C

Christopher Benson-Manica

Richard Heathfield said:
if(line[0] != '#') /* comment - skip it */
{
rc = ProcessLine(line);
}
My alternative looks pretty clean to me. :)

Squeaky, even! I take it back ;)
I don't dispute the convenience of break and continue to the writer of the
code. It is their inconvenience to the /reader/ that gives me pause.

True. Although unlike goto, there's a limit to how convoluted they can get...
 
R

rihad

No. 0 is *a* null pointer constant; (void*)0 is the *other* null
pointer constant. Reread 6.3.2.3#3.

Ok here comes my first lesson on proper usage of English "a" and "the" :)

I still prefer using 0 over NULL for the snipped reasons.
 
D

Dan Pop

In said:
I still prefer using 0 over NULL for the snipped reasons.

No matter what the snipped reasons are, it's a bad idea: NULL makes the
code more readable, because it emphasises the pointer context.

Dan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,093
Messages
2,570,608
Members
47,228
Latest member
ValentinCh

Latest Threads

Top