473,902 Members | 3,601 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Checking return values for errors, a matter of style?

I've written a piece of code that uses sockets a lot (I know that
sockets aren't portable C, this is not a question about sockets per
se). Much of my code ended up looking like this:

Expand|Select|Wrap|Line Numbers
  1. if (function(socket, args) == -1) {
  2. perror("function");
  3. exit(EXIT_FAILURE);
  4. }
I feel that the ifs destroy the readability of my code. Would it be
better to declare an int variable (say succ) and use the following
structure?

Expand|Select|Wrap|Line Numbers
  1. int succ;
  2.  
  3. succ = function(socket, args);
  4. if (succ == -1) {
  5. perror("function");
  6. exit(EXIT_FAILURE);
  7. }
What's considered "best practice" (feel free to substitute with: "what
do most good programmers use")?
Jul 31 '06
66 3679

Keith Thompson wrote:
"Johan Tibell" <jo**********@g mail.comwrites:
Morris Dovey wrote:
IMO, "best practice" is to detect all detectable errors and recover
from all recoverable errors - and to provide a clear explanation
(however terse) of non-recoverable errors.

I have difficulty imagining that any "good programmer" would ignore
errors and/or fail to provide recovery from recoverable errors in
production software.
The topic of this post is not whether to check errors or not.

It is now.
Then quoting my question in your post as if your responding to some
claim I made makes no sense at all.

Aug 2 '06 #41
Sjouke Burry a écrit :
>
The only time I used a debugger in 35 years of
programming, was when an assembler decoding
subroutine failed to cross a 64K barrier properly.
(I did not write that one.).
To me, it smells like "let the system catch my errors"
while you should try to avoid them in the first place.
Sloppy typing can cause errors which are not
found by your compiler/debugger.
Well, is a question of taste. I am not a wizard/genius
programmer and I think I will never find all possible
situations that my software will find. Specially when
interacting with other party software that is underspecified
(like in the case of writing a linker, or an assembler)
there is no other way to know "what is that routine returning"
actually.

And yes, debuggers speed up the development, specially if
used within a good IDE. "Go to definition" "Show usage of"
and many other tools that modern IDEs offer are now a
second nature to me.

Yes, sometimes you do not have a debugger, and then
my first task is usually to write one. Last embedded system
I did was a DSP with 16 bit CPU and 80K RAM. The first thing
I wrote for it was a debugger that used the serial port.

You write all programs correctly in 35 years... never need
a debugger... OK. That's YOUR way of doing things.

In 35 years of coding I never wrote a significant system without having
to go in the debugger to develop it.

Obviously "hello world" type of programs run the first time. But
serious systems?

jacob
Aug 2 '06 #42
Keith Thompson <ks***@mib.orgw rote:
Richard Heathfield <in*****@invali d.invalidwrites :
[...]
The response to a failure depends on the situation. I've covered this in
some detail in my one-and-only contribution to "the literature", so I'll
just bullet-point some possible responses here:

* abort the program. The "student solution" - suitable only for high school
students and, perhaps, example programs (with a big red warning flag).
* break down the memory requirement into two or more sub-blocks.
* use less memory!
* point to a fixed-length buffer instead (and remember not to free it!)
* allocate an emergency reserve at the beginning of the program
* use virtual memory on another machine networked to this one (this
is a lot of work, but it may be worth it on super-huge projects)
[...]

Out of curiosity, how often do real-world programs really do something
fancy in response to a malloc() failure?
Depends on the situation. For example, if the program is a word
processor, then I would hope that the response to a malloc() failure
that occurs when trying to paste a humungous graphic would be to put up
a message saying "sorry, no memory to paste this picture" and keep the
rest of the document in a workable, savable condition, and not to throw
up your hands and die, trashing the still usable existing document.
I've seen suggestions that, if a malloc() call fails, the program can
fall back to an alternative algorithm that uses less memory. How
realistic is this? If there's an algorithm that uses less memory, why
not use it in the first place? (The obvious answer: because it's
slower.) Do programmers really go to the effort of implementing two
separate algorithms, one of which will be used only on a memory
failure (and will therefore not be tested as thoroughly as the primary
algorithm)?
I would expect that for, say, sorting code this is not all that unusual.
Ditto for, say, handling massive queries in database servers.

Richard
Aug 2 '06 #43
rl*@hoekstra-uitgeverij.nl (Richard Bos) writes:
Keith Thompson <ks***@mib.orgw rote:
[...]
>Out of curiosity, how often do real-world programs really do something
fancy in response to a malloc() failure?

Depends on the situation. For example, if the program is a word
processor, then I would hope that the response to a malloc() failure
that occurs when trying to paste a humungous graphic would be to put up
a message saying "sorry, no memory to paste this picture" and keep the
rest of the document in a workable, savable condition, and not to throw
up your hands and die, trashing the still usable existing document.
Thank you, that's an excellent example.

In general, a batch computational program will perform a series of
operations, and if any of them fails it's likely (but by no means
certain) that the best you can do is throw out the whole thing. An
interactive program, on the other hand, performs a series of tasks
that don't necessarily depend on each other, so it makes more sense to
abort one of them and continue with the others.

--
Keith Thompson (The_Other_Keit h) ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <* <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.
Aug 2 '06 #44
Richard Heathfield wrote:
Skarmander said:
>Richard Heathfield wrote:
<snip>
>>Perhaps I should have made it clearer that I'm referring to programs that
are intended to be used over and over and over by lotsa lotsa people. If
it's not worth the programmer's time to write the code robustly, it's not
worth my time to use his program. Of course, it may be worth /his/ time
to use his own program.
You're using "robust" as if it's a yes or no property. You're free to
storm off in a huff shouting "well that's not robust then!" if someone
presents you with a program that can deal with just about anything except
memory exhaustion when it finally comes round the bend, but that doesn't
mean everyone would or should.

<grinNo, there's no harumphing going on over here. But basic resource
acquisition checking is as fundamental to robustness as steering so as not
to bump the kerb is to driving.
Checking, yes. Failure strategies are another matter.

A program that merrily malloc()s along and does not check the return value
is brittle, no argument there. A program that checks and decides to quit at
that point need not be -- but see below.
>>>I'm not saying the tradeoffs involved are always correctly assessed
(it's probably a given that the cost of failure is usually
underestimat ed), but I do believe they exist.
Yes, the cost of failure can be a lost customer, repeated N times.
Don't sell it short. It could be a DEAD CUSTOMER, repeated N times.

Absolutely. And a dead customer is a lost customer, right?
Worse, a dead customer is a lost customer whose relatives will probably
claim. But let us not "bicker and argue over who killed who"...
>Or it could be a single entry in a log somewhere.

Or not even that. We're back to undefined behaviour.
That's not what I meant. A program that deals with memory exhaustion by
exiting cleanly may actually offer enough guarantees to the customer, as far
as the customer is concerned. If that's the case, we needn't worry about
dead customers (as the customer has done this for us), and we'd probably
waste time doing so.
>All this will not make the program more *robust*, however, it'll simply
allow it to make better use of existing resources. ("Simply" should
nevertheless not be taken lightly.) Robustness is given by how well the
system behaves when the resources run dry, not how well it squeezes water
from the remaining stones when there's a drought in sight.

If the objective is to gather enough moisture to survive until the rescue
helicopter arrives - that is, if the objective is to complete the immediate
task so that a consistent set of user data can be saved before you bomb out
- then it's a sensible approach.
Yes, this (make sure you're in a consistent state before bailing out) is the
sense in which robustness applies to memory exhaustion.

Your initial post was confusing because you made it look as if exiting with
an error was unacceptable, as the "high school solution", while what you
meant (I take it) was that a program should respond by working towards a
consistent state, and then (as it eventually will have to) bail out. ("Wait
for more memory to become available" may be an approach for a very limited
set of circumstances, but it's not recommendable in general at all, as it'll
probably lead to deadlocks.)

If the high school student has looked at the program carefully and decided
that, at the point of memory exhaustion, the program state was consistent
enough (and this is given by the specifications, explicit or not), then
simply exiting is altogether acceptable. My argument was that many "high
school exits" are acceptable in just that way. Not all, and exiting as a
blanket approach to everything isn't acceptable, but in many cases that lone
exit() is just what is called for.

Your other recommendations were useful strategies to trade some (often
negligible) performance for more social memory requirements. I see how in
many cases these might be considered as making things more robust in a given
environment, however, so it's mostly arguing semantics.
>>>>* point to a fixed-length buffer instead (and remember not to free it!)
Thread unsafe (unavoidably breaches modularity by aliasing a global, if
you like it more general), increased potential for buffer overflow,
requires checking for an exceptional construct in the corresponding
free() wrapper (I'm assuming we'd wrap this).
Who says it has to be global? It would never have occurred to me to use a
file scope object for such a purpose.
So N fixed-length buffers, then? Obviously N can't be dynamic, or you're
back to square one.

I was assuming just the one, actually, local to the function where the
storage is needed.
My bad, I was thinking in terms of a global approach to memory allocation
(wrappers around malloc() and the like).

S.
Aug 2 '06 #45

jacob navia wrote:
Johan Tibell a écrit :
I've written a piece of code that uses sockets a lot (I know that
sockets aren't portable C, this is not a question about sockets per
se). Much of my code ended up looking like this:

if (function(socke t, args) == -1) {
perror("functio n");
exit(EXIT_FAILU RE);
}

I feel that the ifs destroy the readability of my code. Would it be
better to declare an int variable (say succ) and use the following
structure?

int succ;

succ = function(socket , args);
if (succ == -1) {
perror("functio n");
exit(EXIT_FAILU RE);
}

What's considered "best practice" (feel free to substitute with: "what
do most good programmers use")?

Using the second form allows you to easily see the return value in
the debugger.
Unless the enthusiastic compiler optimised the variable
away (its not really needed in the above example, unless
it gets tested again).

goose,

Aug 2 '06 #46

Johan Tibell wrote:
I've written a piece of code that uses sockets a lot (I know that
sockets aren't portable C, this is not a question about sockets per
se). Much of my code ended up looking like this:

if (function(socke t, args) == -1) {
perror("functio n");
exit(EXIT_FAILU RE);
}

I feel that the ifs destroy the readability of my code. Would it be
better to declare an int variable (say succ) and use the following
structure?

int succ;

succ = function(socket , args);
if (succ == -1) {
perror("functio n");
exit(EXIT_FAILU RE);
}

What's considered "best practice" (feel free to substitute with: "what
do most good programmers use")?
You're in luck :-), here is a slightly modified (incomplete) function
that I wrote (during the course of play:) in the last 30 minutes.

The macros ERROR and DIAGNOSTIC are (currently) identical
and merely print the message to screen (with filename, line
number and function name):

----------------------
#define TEST_INPUT ("test.in")
#define TOK_FOPEN (1)
#define TOK_FERROR (2)
#define TOK_INIT (3)
bool test_token (void)
{
FILE *in = fopen (TEST_INPUT, "r");
jmp_buf handler;
int e, c, counter;

/* All errors caught and handled here */
if ((e = setjmp (handler))!=0) {
switch (e) {
case TOK_FERROR:
ERROR ("read error on '%s', read %i bytes\n",
TEST_INPUT, counter);
break;

case TOK_INIT:
ERROR ("unable to initialise '%s' for reading\n",
TEST_INPUT);
break;

case TOK_FOPEN:
ERROR ("unable to open file '%s' for reading\n",
TEST_INPUT);
break;

default:
ERROR ("unknown error\n");
break;
}
if (in) {
fclose (in); in = NULL;
}
return false;
}
/* Meat of function */
if (!in) longjmp (handler, TOK_FOPEN);

DIAGNOSTIC ("translatio n of '%s' started\n", TEST_INPUT);

c = fgetc (in);
counter = 0;

if (!token_init ()) longjmp (handler, TOK_INIT);

while (c!=EOF) {
counter++;
if (feed_char (c)) {
/* new token awaits us */
}
c = fgetc (in);
}
if (ferror (in)) {
longjmp (handler, TOK_FERROR);
}
printf ("\n");
return true;
}
-----------------------
hth,
goose,

Aug 2 '06 #47

Andrew Poelstra wrote:

<snipped>
programs targeted at home computers or servers can assume that you'll
have a 99.99% success rate on a functioning system when allocating
memory < 1Kb.
Statistically, a 99.99% success rate means that your
program will *certainly* fail in the future.

goose,
Smile, its a joke :-)

Aug 2 '06 #48

Andrew Poelstra wrote:

<snipped>
Because that tiny percentage is the difference between
p = malloc (sizeof *p * q);

and
p = malloc (sizeof *p * q);
if (rv = !!p)
{
/* Rest of function here. */
}

Those ifs nest up and it becomes a pain to manage them.
Then don't; when your number[1] is up and the malloc(10)
call fails, rather exit immediately than have the program
behave unpredictably.

#define FMALLOC(ptr,siz e) (ptr=malloc (size) ? ptr : exit (-1))
....
char *p; FMALLOC(ptr, sizeof *p * q)
....
Tracking UB is a bloody nightmare for the maintainer!!!
Being unable to reproduce the bug is morale-killer.

[1] When your 0.02% or whatever finally comes up.

goose,

Aug 2 '06 #49

Andrew Poelstra wrote:
On 2006-08-01, Richard Heathfield <in*****@invali d.invalidwrote:
Andrew Poelstra said:
It's not so much a matter of style as it is a matter of practibility:
your code must be robust, but it should also be easy to read.
Your code is not going to be robust if it doesn't check whether a request
for an external resource was successful. I agree it should be easy to read,
but that doesn't mean leaving the code out!

What exactly /would/ be the way to do such a thing? I ask you because
you don't like multiple returns or the break statement, both of which
would be a typical response.
See my setjmp/longjmp "solution" above; yes its dirty but it
removes the error recovery code from the logic so that the logic
at least can look clean. It also lets you go mad with error recovery
without you having your logic all messed up.
>
Nowadays
programs targeted at home computers or servers can assume that you'll
have a 99.99% success rate on a functioning system when allocating
memory < 1Kb.
Programmers who make such an assumption should not be writing for the home
market or the server market. They should be writing in crayon on droolproof
paper.

Being as every other post was pretty much exactly as insulting as this,
have you met Dan Pop yet ? :-)
I'd say that I wasn't not wrong on any minor point! I'm glad that I haven't
had the chance to make these foolhardy changes to my actual code yet.
s/not//
This /point/ applies to any resource, not just memory.
<otThats what I battle to get into most programmers
skulls: GC doesn't really help as memory is just another
resource and should be treated as such i.e. the language
(and/or compiler) may let you forget all about managing
memory, but you'll still have to do it resource management
anyway. So don't get too smug about your GC language.
All that will happen is that you'll lose the force-of-habit
action that comes with using malloc and forget to free some
*other* resource.
>
I've written a new interface to my error library so that it will be able
to handle memory failures gracefully, log to a runtime-determined file,
check for bad files or memory, and ensure that a proper message reaches
the user if it can't go on.
Good for you

goose,

Aug 2 '06 #50

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

16
2645
by: lawrence k | last post by:
I've made it habit to check all returns in my code, and usually, on most projects, I'll have an error function that reports error messages to some central location. I recently worked on a project where someone suggested to me I was spending too much time writing error messages, and that I was therefore missing the benefit of using a scripting language. The idea, apparently, is that the PHP interpreter writes all the error messages that are...
0
9845
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
11277
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10866
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10978
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10497
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
1
8045
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5892
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
2
4305
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
3323
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.