To me, wrapping such a simple test in a macro just obscures what is going on by introducing unnecessary abstraction. Making the decision about whether to use a macro in this case seems to me rather like the decision to use operator overloading in C++. There are occasions when you might want to do it to hide complexity, but you'd better have a good reason.
Trust me: I glance at that code, and it is 100% clear to me in that moment what it is doing and why it is doing it--I have been programming primarily in C/C++ now for almost 20 years--but I simply do not believe you that an experienced C/C++ programmer glances at this code and knows 100% for certain that it is correct. If nothing else, clearly the person who wrote this code screwed it up ;P.
This pattern just lends itself to silly mistakes: you might check the error using the wrong constant (!= 0 when you need == -1; this could even work temporarily!) or think that a function can't return an error when it actually can (the setuid mistake that burned Android, see Rage Against the Cage); by using structured error handling you aren't just "hiding" complexity: you are removing it.
Now, I happily admit (and already did in my original comment) that the macro doesn't solve all of these problems as well as "use a language that provides better abstractions (even C++!)"; however, it does mean that you can't accidentally miss the set of parentheses (assigning the comparison result), double up the equal sign (comparing instead of assigning), add an extra semicolon (breaking the comparison, so as to always "goto fail;"), copy/paste the boilerplate incorrectly (which you might have even done in a misguided attempt to avoid the previous errors ;P) leaving you with a second copy of "goto fail;" (as the developer here probably did; it might also have been a merge failure: thankfully, that also becomes impossible, as the error check now is naturally part of the same line as the code being checked), or otherwise make any silly "it still compiles, it looks almost identical, but now it doesn't work" errors. That's valuable :/.
I agree, but then I didn't actually say that I would know it was correct (to any percentage) by glancing at it. What I tried to say was that the "boilerplate" does not detract from the code exposing its meaning or intention.
Again, I agree that the the wrong error constant could be used. But thats also true of a macro is used - especially if the constant is embedded in the macro which is elsewhere.
I didn't want to get into your point about whether this would be better written in another language, because we are where we are. libssl has bee around for a while and has accumulated plenty of dependencies. I don't think a re-write is going to happen any time soon.
TRUE * 1e6
It's horrible code that seems to indicate a lack of production coding standards.Errors, warnings and assertions need to be handled semantically sensibly, obviously and consistently.
Most functions, errors should be handled individually and returned as they happen and the only condition at the very bottom should be success.
My personal fav is anomaly(...) which checks for a nonfatal but suspicious condition. It always throws a warning on STDERR if it fails, even when compiled with -DNDEBUG.
Example: https://gist.github.com/steakknife/9271284
If anyone wants to see decent examples of real C/C++ done right:
nginx [0], doom3 [1], postgres [2], varnish [3] and openssh [4]
References:
[0] http://hg.nginx.org/nginx/file/0251f2f1dc93
[1] https://github.com/TTimo/doom3.gpl
[2] https://github.com/postgres/postgres
if (ctx->main_conf == NULL) {
return NGX_CONF_ERROR;
} /* No MIN_SIZEOF here - we absolutely *must not* truncate the
* username (XXX - so check for trunc!) */
strlcpy(li->username, pw->pw_name, sizeof(li->username));Basically the remark was that the comment went from discussing error handling, to the (much) broader scope of 'C and C++ done "right"', and as an example 5 complete, large open source projects were dumped for us to find the examples ourselves.
if (strlcpy(li->username, pw->pw_name, sizeof(li->username)) >=
sizeof(li->username)) {
error("%s: username too long (%lu > max %lu)", __func__,
(unsigned long)strlen(pw->pw_name),
(unsigned long)sizeof(li->username) - 1);
return NULL;
} rv = strlcpy(dst, src, sizeof(dst));
check(rv == strlen(dst), "Error, src string truncated");
Or perhaps more clear: strlcpy(dst, src, sizeof(dst));
check(strlen(src) == strlen(dst), "Error, src string truncated");