3

For my systems programming class we're doing a lot of programming in C and are required to error check most functions as we are currently learning to program with pthreads.

The reason I say this is not really homework, is that it is far above and beyond what is expected for this class. Simply checking each function individually is more than satisfactory. I just feel this is a time-consuming and messy method and hope for a neater solution.

I was wondering if anyone could show me how to write a function that takes any C function as a parameter, followed by all the required parameters for that function, along with a desired return value (in this case the correct one), and performs the following.

if(function_name(param1, param2, ...) != desired_return_value) {
    fprintf(stderr, "program_name: function_name() failed\n");
    perror("function_name(): ");
}

Is this possible? It's hardly required by our course, but it just irks me that virtually ever function I write has to have 4 lines of code to error check it. It makes it bloody hard to read.

Even some other suggestions would be good. I'm just trying to increase readability, so if this is totally the wrong direction, some correct direction would be much appreciated.

EDIT: This should compile under the gnu99 standard ideally :P

EDIT 2: In response to James McNellis: The errors from our functions do not (I believe in this case), need to be handled. Notification only needs to be supplied. We have covered nothing on handling thread/process related errors (which is this subject in a nutshell).

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ben
  • 2,858
  • 1
  • 18
  • 11
  • 1
    Well this isn't not really homework, but I won't say you didn't say it wasn't. – Alex Sep 14 '10 at 00:57
  • 2
    These aren't not the droids you're not looking for. – detly Sep 14 '10 at 01:10
  • In my experience, in a lot of cases, but especially in threading code, not handling errors is a sure path to early baldness, headaches, and many hours of late night debugging. The problem is the high likelihood of a long lag time between writing the code that violates some assumption, and the code that depends on it. – RBerteig Sep 14 '10 at 07:18

1 Answers1

13

Writing generic code in C without using macros isn't the easiest thing to do.

For a (very) basic solution using a variadic macro:

#define CALL_AND_CHECK(f, r, ...)                                \
    do {                                                         \
        if (f(__VA_ARGS__) != r)                                 \
        {                                                        \
            fprintf(stderr, "program_name: " #f "() failed\n");  \
            perror(#f "(): ");                                   \
        }                                                        \
    } while (0)

(See Why are there sometimes meaningless do/while and if/else statements in C and C++ macros? for why the "meaningless" do/while loop is used)

Note that printing out an error message and not actually handling the error is almost certainly a bad idea. Generally, different errors need to be handled in different ways, so generic code like this may not be particularly useful. If you don't want to try and recover from any of these errors, you could exit(), which might be okay for an assignment, though in a real-world program you wouldn't want to do that.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • @James McNellis We've had massive spiels on how macros are bad, but no real justification. Under what circumstances would this be a problem? Or what are the problems inherent that I should look up and be aware of before using macros. – Ben Sep 14 '10 at 01:02
  • @Ben: Macros are bad when they make code harder to understand. They are good when they reduce the amount of code that you have to write and when they make code easier to understand. It's often quite subjective whether a given use is a good idea or not. – James McNellis Sep 14 '10 at 01:07
  • Cheers. Gonna leave this open for a while and see if some other answers come in. – Ben Sep 14 '10 at 01:13
  • 2
    @Ben: this macro is fairly well-behaved: it evaluates each argument exactly once, thus avoiding one surprising thing that macros can do. You must either avoid using it like this: `if (condition) CALL_AND_CHECK(f,r,a,b,c) else CALL_AND_CHECK(f,r,x,y,z);`, which doesn't work, or add the usual macro do/while(0) trick. The inherent problem with macros is that they look like function calls but they're not, they're just text replacement, so they don't interact with the syntax of C in a very intuitive way. – Steve Jessop Sep 14 '10 at 01:46
  • @Steve Jessop So as long as the code in the macro can be directly substituted into the place within my program where the macro is called, this should behave correctly? – Ben Sep 14 '10 at 01:59
  • @Ben: yes, it's just a question of writing your macros to minimise the chance that in future, you'll forget the restrictions on how they're supposed to be used. If you ever write if/else statements without curly brackets, it's easy to accidentally misuse macros that can attach themselves to an `else`. – Steve Jessop Sep 14 '10 at 02:15
  • @James McNellis Thanks for the quick response. Seems to be the way to do it :) – Ben Sep 14 '10 at 02:17
  • 1
    The macro above really should have the `do..while(0)` to be called well-behaved. – bstpierre Sep 14 '10 at 02:37
  • @bstpierre: Why? Isn't the purpose of the do..while(0) hack to ensure that the macro has its own scope "body", which it already does due to the if(..){..}? – Arafangion Sep 14 '10 at 02:38
  • 1
    @Arafangion - no, it's to ensure that it works correctly with a semicolon (and, eg. doesn't "claim" am else statement or suchlike). – detly Sep 14 '10 at 02:43
  • @detly This should not be able to claim an else statement unless the code is written without braces (which I avoid). I am under the impression this is bad practice to write such code. If the code has braces the else will be assigned correctly to the preceding if, not the one in the macro. – Ben Sep 14 '10 at 02:56
  • 2
    @Ben - sure, but if you use the `do ... while(0)` form, you (or anyone else) will never have to worry about arbitrarily restricting valid C syntax anyway. But more to the point, sometimes you think "oh, I'll just put in a one line `if/else` for now..." and then spend two days debugging some seemingly bizarre problem. (At any rate, I really just wanted to correct the assertion above for the benefit of anyone else reading it.) – detly Sep 14 '10 at 03:03
  • @detly Fair call. I am guilty of using bad coding practice for temporary changes. Steve has pointed out the restrictions for usage and I'll probably use the `do ... while(0)` form when implementing this with the intention of saving possible future headaches just in case. Thanks. – Ben Sep 14 '10 at 03:06
  • I think Steve makes the most important point, though: the critical word in "function-like macro" is "like." Invoking a function-like macro doesn't result in a function call, it results in a token substitution. – James McNellis Sep 14 '10 at 03:22
  • To be honest, with all the talk there is about how useful the `do...while(0)` idiom is, I have never had a bug caused by not using it. Granted, some people here have been writing code in C since before I was born, so I'd tend to trust their advice based on experience more than I'd trust mine... – James McNellis Sep 14 '10 at 03:28
  • @James McNellis - it's more a problem when code is written by different people, at least one of whom assumes that everyone else uses the `do ... while(0)` trick... – detly Sep 14 '10 at 04:40
  • I think "arbitrarily restricting" usage is a good observation. Given the behaviour of the macro, it could be implemented `if (foo) { bar }`, or `if (foo) bar`, or `foo ? bar : (void)0`, or `(foo ? bar : (void)0)`, or `do { if (foo) bar; } while(0)`. (There are two statements in this case, but `bar` could be both of them, comma-separated, or a function call). Some forms impose more annoying restrictions than others. Rather than make people wonder what they can and can't do with your macro, the intent of the do/while(0) trick is to be user-friendly by imposing light, "natural", restrictions. – Steve Jessop Sep 14 '10 at 12:35
  • I don't think I've ever had a serious, time-consuming bug caused by a macro grabbing an `else`, but I think I have misused other people's macros before in this way. I'm certain that I've "misused" macros which evaluate to expressions, where those expressions don't have surrounding parens and can result in screwy operator precedence. In those cases it's a lot more clear-cut that the macro itself is unfriendly/buggy, rather than my usage being optimistic. I consider both to be issues of hygiene, though. – Steve Jessop Sep 14 '10 at 12:40
  • Oh, and for the record, I think I might just barely have been writing code since before James was born, but certainly not C code, so you don't have to accept my advice on those grounds :-) – Steve Jessop Sep 14 '10 at 12:44
  • There is no good reason other than laziness not to use the do...while(0), so I've added it as I was just reminded that one shouldn't be lazy, even in example code. That said, it would probably also be useful to use an indirect stringize macro in the event that `f` is actually itself a macro name; it depends on how the macro is intended to be used and what is best for a user of the program to see. – James McNellis Sep 14 '10 at 19:13