The problem is that the above is a remarkably simple case. The reality for a lot of cases where goto gets used for error handling is that with the above what you would end up writing is:
bool success = doSomethingThatMightFail();
if (success) {
success = doSomethingElseThatMightFail();
if (success) {
success = doMoreFailingStuff();
if (success) {
success = yepMoreFailingStuff();
if (success) { ...
}
}
}
}
if (!success) {
cleanupWork
}
Meanwhile with a reasonably constructed macro to wrap his pattern, you end up with:
bool success = true;
CHK(doSomethingThatMightFail());
CHK(doSomethingElseThatMightfail());
CHK(doMoreFailingStuff());
CHK(yepMoreFailingStuff());
end:
if (!success) {
cleanupWork
}
That's much easier to read, and just as safe.
Don't get me wrong, in a language where you can use exceptions for this, or better yet, the error monad, you should absolutely use those more abstracted concepts, but in plain C, this really is the best approach to handling errors in code where everything you do could go wrong.