Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a good programming pattern for handling return values from stdio file writing functions

Tags:

c

stdio

I'm working on some code that generates a lot of

ignoring return value of ‘size_t fwrite(const void*, size_t, size_t, FILE*)’, declared with attribute warn_unused_result

warnings when compiled with g++, and I'm wondering about the best programming pattern to actually record and handle the return value of a large number of separate sequential fwrites (i.e. not the same fwrite in a loop)

Let's say that the code looks like this at the moment:

fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...

I'm currently thinking about something like this, but I may have difficulty cleaning up the file pointer:

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) return someerrorcode;
// ... more code ...
if (fwrite (&foo, sizeof (foo), 1, fp) != 1) return someerrorcode;
// ... more code ...

I think that approach is clearly better than nesting, which would get too crazy too quick:

if (fwrite (&blah, sizeof (blah), 1, fp) == 1) {
   // ... more code ...
   if (fwrite (&foo, sizeof (foo), 1, fp) == 1) {;
      // ... more code ...
   }
}

Surely there is already an established best-practice pattern for this sort of thing, though?

Of course, as I am mainly looking into this to get rid of the compiler warnings, I could just assign the return value to a dummy variable and ignore it, but I'd like to try doing it the right way first.

dummy = fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
dummy = fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...

Update: I've removed the c++ tag as this code is really just c being compiled using g++, so c based solutions are needed to keep with the rest of the code base.

like image 579
David Dean Avatar asked Feb 20 '09 13:02

David Dean


2 Answers

I'd do something along these lines:

FILE * file = fopen("foo", "wb");
if(!file) return FAILURE;

// assume failure by default
_Bool success = 0;

do
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]

    success = 1;
} while(0);

fclose(file);

return success ? SUCCESS : FAILURE;

With a little C99 macro magic

#define with(SUBJECT, FINALIZE, ...) do { \
    if(SUBJECT) do { __VA_ARGS__ } while(0); if(SUBJECT) FINALIZE; \
} while(0)

and using ferror() instead of our own error flag as suggested by Jonathan Leffler, this can be written as

FILE * file = fopen("foo", "wb");
with(file, fclose(file),
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]
});

return file && !ferror(file) ? SUCCESS : FAILURE;

If there are other error conditions aside from io errors, you'll still have to track them with one or more error variables, though.

Also, your check against sizeof(blah) is wrong: fwrite() returns the count of objects written!

like image 132
Christoph Avatar answered Oct 21 '22 19:10

Christoph


The poor man's C exception handling based on goto (in fact, the one and only instance of goto NOT being harmful):

int foo() {
    FILE * fp = fopen(...);
    ....

    /* Note: fwrite returns the number of elements written, not bytes! */
    if (fwrite (&blah, sizeof (blah), 1, fp) != 1) goto error1;

    ...

    if (fwrite (&foo, sizeof (foo), 1, fp) != 1) goto error2;

    ...

ok:
    /* Everything went fine */
    fclose(fp);
    return 0;

error1:
    /* Error case 1 */
    fclose(fp);
    return -1;

error2:
    /* Error case 2 */
    fclose(fp);
    return -2;
}

You get the idea. Restructure as you wish (single/multiple returns, single cleanup, custom error messages, etc.). From my experience this is the most common C error handling pattern out there. The crucial point is: NEVER, EVER ignore stdlib return codes, and any good reason to do so (e.g. readability) is not good enough.

like image 33
fbonnet Avatar answered Oct 21 '22 21:10

fbonnet