I'm trying to learn C by writing a simple parser / compiler. So far its been a very enlightening experience, however coming from a strong background in C# I'm having some problems adjusting - in particular to the lack of exceptions.
Now I've read Cleaner, more elegant, and harder to recognize and I agree with every word in that article; In my C# code I avoid throwing exceptions whenever possible, however now that I'm faced with a world where I can't throw exceptions my error handling is completely swamping the otherwise clean and easy-to-read logic of my code.
At the moment I'm writing code which needs to fail fast if there is a problem, and it also potentially deeply nested - I've settled on a error handling pattern whereby "Get" functions return NULL on an error, and other functions return -1 on failure. In both cases the function that fails calls NS_SetError() and so all the calling function needs to do is to clean up and immediately return on a failure.
My issue is that the number of if (Action() < 0) return -1; statements that I have is doing my head in - it's very repetitive and completely obscures the underlying logic. I've ended up creating myself a simple macro to try and improve the situation, for example:
#define NOT_ERROR(X) if ((X) < 0) return -1
int NS_Expression(void)
{
NOT_ERROR(NS_Term());
NOT_ERROR(Emit("MOVE D0, D1\n"));
if (strcmp(current->str, "+") == 0)
{
NOT_ERROR(NS_Add());
}
else if (strcmp(current->str, "-") == 0)
{
NOT_ERROR(NS_Subtract());
}
else
{
NS_SetError("Expected: operator");
return -1;
}
return 0;
}
Each of the functions NS_Term, NS_Add and NS_Subtract do a NS_SetError() and return -1 in the case of an error - its better, but it still feels like I'm abusing macros and doesn't allow for any cleanup (some functions, in particular Get functions that return a pointer, are more complex and require clean-up code to be run).
Overall it just feels like I'm missing something - despite the fact that error handling in this way is supposedly easier to recognize, In many of my functions I'm really struggling to identify whether or not errors are being handled correctly:
Some functions return NULL on an error
Some functions return < 0 on an error
Some functions never produce an error
My functions do a NS_SetError(), but many other functions don't.
Is there a better way that I can structure my functions, or does everyone else also have this problem?
Also is having Get functions (that return a pointer to an object) return NULL on an error a good idea, or is it just confusing my error handling?
It's a bigger problem when you have to repeat the same finalizing code before each return from an error. In such cases it is widely accepted to use goto:
int func ()
{
if (a() < 0) {
goto failure_a;
}
if (b() < 0) {
goto failure_b;
}
if (c() < 0) {
goto failure_c;
}
return SUCCESS;
failure_c:
undo_b();
failure_b:
undo_a();
failure_a:
return FAILURE;
}
You can even create your own macros around this to save you some typing, something like this (I haven't tested this though):
#define CALL(funcname, ...) \
if (funcname(__VA_ARGS__) < 0) { \
goto failure_ ## funcname; \
}
Overall, it is a much cleaner and less redundant approach than the trivial handling:
int func ()
{
if (a() < 0) {
return FAILURE;
}
if (b() < 0) {
undo_a();
return FAILURE;
}
if (c() < 0) {
undo_b();
undo_a();
return FAILURE;
}
return SUCCESS;
}
As an additional hint, I often use chaining to reduce the number of if's in my code:
if (a() < 0 || b() < 0 || c() < 0) {
return FAILURE;
}
Since || is a short-circuit operator, the above would substitute three separate if's. Consider using chaining in a return statement as well:
return (a() < 0 || b() < 0 || c() < 0) ? FAILURE : SUCCESS;
One technique for cleanup is to use an while loop that will never actually iterate. It gives you goto without using goto.
#define NOT_ERROR(x) if ((x) < 0) break;
#define NOT_NULL(x) if ((x) == NULL) break;
// Initialise things that may need to be cleaned up here.
char* somePtr = NULL;
do
{
NOT_NULL(somePtr = malloc(1024));
NOT_ERROR(something(somePtr));
NOT_ERROR(somethingElse(somePtr));
// etc
// if you get here everything's ok.
return somePtr;
}
while (0);
// Something went wrong so clean-up.
free(somePtr);
return NULL;
You lose a level of indentation though.
Edit: I'd like to add that I've nothing against goto, it's just that for the use-case of the questioner he doesn't really need it. There are cases where using goto beats the pants off any other method, but this isn't one of them.
You're probably not going to like to hear this, but the C way to do exceptions is via the goto statement. This is one of the reasons it is in the language.
The other reason is that goto is the natural expression of the implementation of a state machine. What common programming task is best represented by a state machine? A lexical analyzer. Look at the output from lex sometime. Gotos.
So it sounds to me like now is the time for you to get chummy with that parriah of language syntax elements, the goto.
Besides goto, standard C has another construct to handle exceptional flow control setjmp/longjmp. It has the advantage that you can break out of multiply nested control statements more easily than with break as was proposed by someone, and in addition to what goto provides has a status indication that can encode the reason for what went wrong.
Another issue is just the syntax of your construct. It is not a good idea to use a control statement that can inadvertibly be added to. In your case
if (bla) NOT_ERROR(X);
else printf("wow!\n");
would go fundamentally wrong. I'd use something like
#define NOT_ERROR(X) \
if ((X) >= 0) { (void)0; } \
else return -1
instead.
THis must be thought on at least two levels: how your functions interact, and what you do when it breaks.
Most large C frameworks I see always return a status and "return" values by reference (this is the case of the WinAPI and of many C Mac OS APIs). You want to return a bool?
StatusCode FooBar(int a, int b, int c, bool* output);
You want to return a pointer?
StatusCode FooBar(int a, int b, int c, char** output);
Well, you get the idea.
On the calling function's side, the pattern I see the most often is to use a goto statement that points to a cleanup label:
if (statusCode < 0) goto error;
/* snip */
return everythingWentWell;
error:
cleanupResources();
return somethingWentWrong;
What about this?
int NS_Expression(void)
{
int ok = 1;
ok = ok && NS_Term();
ok = ok && Emit("MOVE D0, D1\n");
ok = ok && NS_AddSub();
return ok
}
The short answer is: let your functions return an error code that cannot possibly be a valid value - and always check the return value. For functions returning pointers, this is NULL. For functions returning a non-negative int, it's a negative value, commonly -1, and so on...
If every possible return value is also a valid value, use call-by-reference:
int my_atoi(const char *str, int *val)
{
// convert str to int
// store the result in *val
// return 0 on success, -1 (or any other value except 0) otherwise
}
Checking the return value of every function might seem tedious, but that's the way errors are handled in C. Consider the function nc_dial(). All it does is checking its arguments for validity and making a network connection by calling getaddrinfo(), socket(), setsockopt(), bind()/listen() or connect(), finally freeing unused resources and updating metadata. This could be done in approximately 15 lines. However, the function has nearly 100 lines due to error checking. But that's the way it is in C. Once you get used to it, you can easily mask the error checking in your head.
Furthermore, there's nothing wrong with multiple if (Action() == 0) return -1;. To the contrary: it is usually a sign of a cautious programmer. It's good to be cautious.
And as a final comment: don't use macros for anything but defining values if you can't justify their use while someone is pointing with a gun at your head. More specifically, never use control flow statements in macros: it confuses the shit out of the poor guy who has to maintain your code 5 years after you left the company. There's nothing wrong with if (foo) return -1;. It's simple, clean and obvious to the point that you can't do any better.
Once you drop your tendency to hide control flow in macros, there's really no reason to feel like you're missing something.
A goto statement is the easiest and potentially cleanest way to implement exception style processing. Using a macro makes it easier to read if you include the comparison logic inside the macro args. If you organize the routines to perform normal (i.e. non-error) work and only use the goto on exceptions, it is fairly clean for reading. For example:
/* Exception macro */
#define TRY_EXIT(Cmd) { if (!(Cmd)) {goto EXIT;} }
/* My memory allocator */
char * MyAlloc(int bytes)
{
char * pMem = NULL;
/* Must have a size */
TRY_EXIT( bytes > 0 );
/* Allocation must succeed */
pMem = (char *)malloc(bytes);
TRY_EXIT( pMem != NULL );
/* Initialize memory */
TRY_EXIT( initializeMem(pMem, bytes) != -1 );
/* Success */
return (pMem);
EXIT:
/* Exception: Cleanup and fail */
if (pMem != NULL)
free(pMem);
return (NULL);
}
It never occurred to me to use goto or do { } while(0) for error handling in this way - its pretty neat, however after thinking about it I realised that in many cases I can do the same thing by splitting the function out into two:
int Foo(void)
{
// Initialise things that may need to be cleaned up here.
char* somePtr = malloc(1024);
if (somePtr = NULL)
{
return NULL;
}
if (FooInner(somePtr) < 0)
{
// Something went wrong so clean-up.
free(somePtr);
return NULL;
}
return somePtr;
}
int FooInner(char* somePtr)
{
if (something(somePtr) < 0) return -1;
if (somethingElse(somePtr) < 0) return -1;
// etc
// if you get here everything's ok.
return 0;
}
This does now mean that you get an extra function, but my preference is for many short functions anyway.
After Philips advice I've also decided to avoid using control flow macros as well - its clear enough what is going on as long as you put them on one line.
At the very least Its reassuring to know that I'm not just missing something - everyone else has this problem too! :-)
Use setjmp.
http://en.wikipedia.org/wiki/Setjmp.h
http://aszt.inf.elte.hu/~gsd/halado_cpp/ch02s03.html
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
#include <setjmp.h>
#include <stdio.h>
jmp_buf x;
void f()
{
longjmp(x,5); // throw 5;
}
int main()
{
// output of this program is 5.
int i = 0;
if ( (i = setjmp(x)) == 0 )// try{
{
f();
} // } --> end of try{
else // catch(i){
{
switch( i )
{
case 1:
case 2:
default: fprintf( stdout, "error code = %d\n", i); break;
}
} // } --> end of catch(i){
return 0;
}
#include <stdio.h>
#include <setjmp.h>
#define TRY do{ jmp_buf ex_buf__; if( !setjmp(ex_buf__) ){
#define CATCH } else {
#define ETRY } }while(0)
#define THROW longjmp(ex_buf__, 1)
int
main(int argc, char** argv)
{
TRY
{
printf("In Try Statement\n");
THROW;
printf("I do not appear\n");
}
CATCH
{
printf("Got Exception!\n");
}
ETRY;
return 0;
}
Related
There are two main ways,which is better?
Deal with error right now.
int func(){
rv = process_1();
if(!rv){
// deal with error_1
return -1;
}
rv = process_2();
if(!rv){
// deal with error_1
// deal with error_2
return -1;
}
return 0;
}
Deal with errors at go-to. I found a lot of this style of code in the Linux kernel code.
int func(){
rv = process_1();
if(!rv){
goto err_1
}
rv = process_2();
if(!rv){
goto err_2;
}
return 0;
err_2:
// deal with error_2
err_1:
// deal with error_1
return -1;
}
This is really prone to become a flame war, but here my opinion :
A lot of people will say that goto is inherently evil, that you should never use it.
While I can agree to a certain degree, I also can say that when it come to clean multiple variable (like by using fclose / free / etc etc), I find goto to be the cleanest (or more readable, at least) way of doing it.
To be clear, I advise to always use the simplest way for error handling, not using always goto.
For exemple,
bool MyFunction(void)
{
char *logPathfile = NULL;
FILE *logFile = NULL;
char *msg = NULL;
bool returnValue = false;
logPathfile = malloc(...);
if (!logPathfile) {
// Error message (use possibly perror (3) / strerror (3))
goto END_FUNCTION;
}
sprintf(logPathfile, "%s", "/home/user/exemple.txt");
logFile = fopen(logPathfile, "w");
if (!logFile) {
// Error message (use possibly perror (3) / strerror (3))
goto END_FUNCTION;
}
msg = malloc(...);
if (!msg) {
// Error message (use possibly perror (3) / strerror (3))
goto END_FUNCTION;
}
/* ... other code, with possibly other failure test that end with goto */
// Function's end
returnValue = true;
/* GOTO */END_FUNCTION:
free(logPathfile);
if (logFile) {
fclose(logFile);
}
free(msg);
return returnValue;
}
By using goto to handle the error, you now really reduce the risk to do memory leak.
And if in the futur you have to add another variable that need cleaning, you can add the memory management really simply.
Or if you have to add another test (let's say for example that the filename should not begin by "/root/"), then you reduce the risk to forgetting to free the memory because the goto whill handle it.
Like you said it, you can also use this flow structure to add rollback action.
Depending the situation, you maybe don't need to have multiple goto label thougth.
Let's say that in the previous code, if there is an error, we have to delete the created file.
Simply add
/* rollback action */
if (!returnValue) {
if (logPathfile) {
remove(logPathfile);
}
}
rigth after the goto label, and you're done :)
=============
edit :
The complexity added by using goto are, as far as I know, the following :
every variable that will be cleaned or use to use clean have to be intialized.
That should not be problematic since setting pointer to a valid value (NULL or other) should always be done when declaring the variable.
for example
void MyFunction(int nbFile)
{
FILE *array = NULL;
size_t size = 0;
array = malloc(nbFile * sizeof(*array));
if (!array) {
// Error message (use possibly perror (3) / strerror (3))
goto END_FUNCTION;
}
for (int i = 0; i < nbFile; ++i) {
array[i] = fopen("/some/path", "w");
if (!array[i]) {
// Error message (use possibly perror (3) / strerror (3))
goto END_FUNCTION;
}
++size;
}
/* ... other code, with possibly other failure test that end with goto */
/* GOTO */END_FUNCTION:
/* We need size to fclose array[i], so size should be initialized */
for (int i = 0; i < size; ++i) {
flcose(array[i]);
}
free(array);
}
(yeah, I know that If I had use calloc instead of malloc, I could have tested if array[i] != NULL to know if I need to fclose, but it's for the sake of the explanation ...)
You probably have to add another variable for the function return value.
I usually set this variable to indicate failure at the beginning (like setting false) and give it's success value just before the goto.
Sometime, in some situation, this can seem weird, but it's, in my opinion, still understandable (just add a comment :) )
I'd recommend you to read thoroughly the examples you have found (more if they are in the kernel code of an operating system.) The situation you describe corresponds to an algorithm that should make decisions at each stage of the execution, and those stages require to undo the previous steps.
You first allocate some resource #1, and continue.
then you allocate another resource (say resource #2) if that fails, then you have to free resource #1, as it is not longer valid.
...
finally you allocate resource #N, if that fails you must free resources #1 to #N-1.
The figure you show allows you to write in one line, a set of resource allocations, between which you have to decide if you continue.
In this scenario a policy like this is recommended (for novice C programmers, as it avoids the use of goto but becomes less readable (as it nests as things happen)
if ((res_1 = some_allocation(blablah)) != ERROR_CODE) {
if ((res_2 = some_other_allocation(blablatwo)) != ANOTHER_ERROR_CODE) {
...
if ((res_N = some_N_allocation(blablaN)) != NTH_ERROR_CODE) {
do_what_is_needed();
return_resource_N(res_N); /* free resN */
} else {
do_action_corresponding_to_failed_N(); /* error for failing N */
}
return_resource_N_minus_one(resN_1); /* free resN_1 */
...
} else {
do_action_corresponding_to_failed_2(); /* error for failing #2 */
}
return_resource_1(res1); /* free #1. (A): (see below) */
} else {
do_acttion_corresponding_to_failed_1(); /* error for failing #1 */
}
/* there's nothing to undo here, as we have returned the first resource in (A) above. */
nothing to say about this code, but that it has no gotos, but is incredible far less readable (it's a mess of nested things in which, when you fail for resource N, then you have to return up to N-1 resources.) you can messup the resources deallocated by putting them in the wrong position and it's error prone. But on the other side, it allocates and deallocates the things in just one place and is as compact as the code with gotos.
writing this code with gotos gives this:
if ((res_1 = some_allocation(blablah)) == ERROR_CODE) {
do_acttion_corresponding_to_failed_1(); /* error for failing #1 */
goto end;
}
if ((res_2 = some_other_allocation(blablatwo)) == ANOTHER_ERROR_CODE) {
do_action_corresponding_to_failed_2(); /* error for failing #2 */
goto res1;
}
...
if ((res_N = some_N_allocation(blablaN)) == NTH_ERROR_CODE) {
do_action_corresponding_to_failed_N(); /* error for failing #N */
goto resN1;
}
do_what_is_needed();
return_resource_N(res_N); /* free resN */
resN1: return_resource_N_minus_one(resN_1); /* free resN_1 */
...
res1: return_resource_1(res1); /* free #1. (A): (see below) */
end: /* there's nothing to undo here, as we have returned the first resource in (A) above. */
There's only thing that can be said about the first code that will make it perform better in some architectures. Dealing with goto is a pain for the compiler, as normally it has to make assumptions about all the possible resulting blocks that will end jumping to the same label, and this makes things far more difficult to optimice, resulting in not so optimiced code. (this is clear when you use structured blocks, and only implies one or two places you can come from), and you will get worse performance code (not much worse, but somewhat slower code)
You will agree with me that the equivalent code you post in your code is more readable, probably exactly the same level of correctness.
Other required use of goto constructs is when you have several nested loops and you have to exit more than the closest loop to exit.
for(...) {
for(...) {
...
for (...) {
goto out;
}
...
}
}
out:
this is also C specific, as other languages allow you to label the construct you want to exit from and specify it in the break statement.
E.g. in Java:
external_loop: for(...) {
for(...) {
...
for (...) {
break external_loop;
}
...
}
}
In this case you don't need to jump, as the break knows how many loops we need to exit.
One last thing to say. With just the while() construct, all other language constructs can be simulated, by introducing state variables to allow you to do things (e.g. stepping out of each loop by checking some variable used precisely for that). And even less.... if we allow for recursive function call, even the while() loop can be simulated, and optimicers are capable of guessing a faster implementation without recursion for the simulated block. Why in the schools nobody says never use if sentences, they are evil? This is because there's a frequent fact that newbies tend to learn one struct better than others and then, they get the vice of using it everywhere. This happens frequently with goto and not with others, more difficult to understand but easier to use, once they have been understood.
The use of goto for everything (this is the legacy of languages like assembler and early fortran) and maintaining that code normally ends in what is called spaghetti programming. A programmer just selects at random a place to write his/her code in the main code of a program, opens an editor and inserts it's code there:
Let's say that we have to do several steps, named A to F:
{
code_for_A();
code_for_B();
code_for_C();
code_for_D();
code_for_E();
code_for_F();
}
and later, some steps, named G and H have to be added to be executed at the end. Spaghetti programming can make the code end being something like this:
{
code_for_A();
code_for_B();
code_for_C(); /* programmer opened the editor in this place */
goto A;-------.
|
B:<---------------+-.
code_for_G(); | | /* the code is added in the middle of the file */
code_for_H(); | |
goto C;-------+-+--.
| | |
A:<---------------' | |
code_for_D(); | |
code_for_E(); | |
code_for_F(); | |
goto B; --------' |
|
C:<--------------------'
}
While this code is correct (it executes steps A to H in sequence), it will take a programmer some time to guess how the code flows from A to H, by following back and forward the gotos.
For an alternate open that can sometimes be used to "hide" the gotos, one of our programmers got us using what he calls "do once" loops. They look like this:
failed = true; // default to failure
do // once
{
if( fail == func1(parm1) )
{ // emit error
break;
}
failed = false; // we only succeed if we get all the way through
}while(0);
// do common cleanup
// additional failure handling and/or return success/fail result
Obviously, the if block inside the 'do once' would be repeated. For example, we like this structure for setting up a network connection because there are many steps that have the possibility of failure. This structure can get tricky to use if you need a switch or another loop embedded within, but it has proven to be a surprisingly handy way to deal with error detection and common cleanup for us.
If you hate it, don't use it. (smile) We like it.
Is there some well known pattern/practice for nested error handling in C, something like nested exceptions in Java?
With the usual "just return error code/success" error details may be lost before a program can determine it should log/report error.
Imagine a code similar to this:
err B()
{
if (read(a/b/c/U.user) != OK) {
return read_error; //which would be eaccess or we could return even e_cannot_read_user
}
if (is_empty(read_user.name)) {
// we could tell exactly what is missing here
return einval;
}
...
}
err A()
{
if (B() != OK) {
if (cannot_handle_B_failing()) {
return e_could_not_do_b;
}
}
...
}
main()
{
...
if (A() != OK) && (no_alternative_solution()) {
report error_returned_by_A;
wait_for_more_user_input();
}
}
Has anyone successfully tried some kind of nested error codes/messages in C for situations like that? Something that could report (in main) the fact that user name was missing or that file F can not be read due to invalid permissions.
Is there a library to support something like this?
I would suggest you to look at Apple's error handling guideline. It was designed for Objective-C and the main class there is NSError. They are using a userInfo dictionary (map) for holding detailed info about the error, and they have predefined NSUnderlyingErrorKey constant for holding underlying NSError object in that dictionary if needed.
So you can declare your own error struct for your code and implement similar solution.
e.g.
typedef struct {
int code;
struct Error *underlyingError;
char domain[0];
} Error;
You can then use domain field to categorize errors (by libs, files or functions as you want); code field to determine error itself and optional underlyingError field to find out what underlying error caused the error you received.
Each function may have its own independent, documented, and isolated set of errors. Like each function from the libc have their own documented set of possible return values and ERRNO codes.
The "root cause" is only an implementation detail, you just have to know "why" it failed.
In other words, A's documentation should not explain B, should not tell it uses B, nor tell about B's errors codes, it can have its own, locally meaningful, error codes.
Also while trying alternatives, you'll have to keep the origin failure codes (locally), so if the alternatives also fail you'll still be able to know what caused you to try them in the first place.
err B()
{
if (read(a/b/c/U.user) != OK) {
return read_error; //which would be eaccess or we could return even e_cannot_read_user
}
if (is_empty(read_user.name)) {
// we could tell exactly what is missing here
return einval;
}
...
}
err A()
{
if ((b_result = B()) != OK) {
// Here we understand b_result as we know B,
// but outside of we will no longer understand it.
// It means that we have to map B errors
// to semantically meaningful A errors.
if (cannot_handle_B_failing()) {
if (b_result == …)
return e_could_not_do_b_due_to_…;
else if (b_result == …)
return e_could_not_do_b_due_to_…;
else
return e_could_not_do_b_dont_know_why;
}
}
...
}
main()
{
...
if ((a_result = A()) != OK) && (no_alternative_solution()) {
// Here, if A change its implementation by no longer calling B
// we don't care, it'll still work.
report a_result;
wait_for_more_user_input();
}
}
It's costly to map B's errors to A's errors, but there's a profit: when B will change its implementation, it won't break all A's call sites.
This semantical mapping may look useless at first ("I'll map a "permission denied" to a "permission denied"...) but has to be adapted to the current level of abstraction, typically from a "cannot open file" to an "cannot open configuration", like:
err synchronize(source, dest, conf) {
conf_file = open(conf);
if (conf == -1)
{
if (errno == EACCESS)
return cannot_acces_config;
else
return unexpected_error_opening_config_file;
}
if (parse(config_file, &config_struct) == -1)
return cannot_parse_config;
source_file = open(source);
if (source_file == -1)
{
if (errno == EACCESS)
return cannot_open_source_file;
else
return unexpected_error_opening_source_file;
}
dest_file = open(dest);
if (dest == -1)
{
if (errno == EACCESS)
return cannot_open_dest_file;
else
return unexpected_error_opening_dest_file;
}
}
And it does not have to be a one to one mapping. If you map errors one-to-one, for a depth of three functions, with three calls each, with the deeper function having 16 different possible errors, it'll map to 16 * 3 * 3 = 144 different distinct errors, which is just a maintenance hell for everyone (imagine your translators having to translate 144 error messages too… and your documentation listing and explaining them all, for a single function).
So, do not forget that functions have to abstract the work they're doing and also abstract the errors they encounter, to an understandable, locally meaningful, set of errors.
Finally, in some cases, even by keeping a whole stack trace of what happened, you won't be able to deduce the root cause of an error: Imagine a configuration reader have to look for configuration in 5 different places, it may encounter 3 "file not found", one "permission denied", and another "file not found", so it will return "Configuration not found". From here, nobody but the user can tell why it failed: Maybe the user did a typo in the first file name, and the permission denied was totally expected, or maybe the first three files are not meant to exist but the user did a chmod error on the 4th one.
In those cases, the only way to help the user debugging the issue is to provide verbose flags, like "-v" , "-vv", "-vvv", … each time adding a new level of debugging details, up to a point where the user will be able to see in the logs that the configuration had 5 places to check, checked the first one, got a file not found, and so on, and deduce where the program diverged from its intentions.
The solution we use in one of our project is to pass special error-handling struct thru full stack of functions. This allows to get original error and message on any higher level. Using this solution your example will look like:
struct prj_error {
int32_t err;
char msg[ERR_MAX_LEN];
};
prj_error_set(struct prj_error *err, int errorno, const char *fmt, ...); /* implement yourselves */
int B(struct prj_error *err)
{
char *file = "a/b/c/U.user";
if (custom_read(file) != OK) {
prj_error_set(err, errno, "Couldn't read file \"%s\". Error: %s\n",
file, strerror(errno));
return err->err;
}
if (is_empty(read_user.name)) {
prj_error_set(err, -ENOENT, "Username in file \"%s\" is empty\n",
file);
return err->err;
}
...
}
int A(struct prj_error *err)
{
if (B(err) != OK) {
if (cannot_handle_B_failing()) {
return err.err;
}
}
...
}
main()
{
struct prj_error err;
...
if (A(&err) != OK) && (no_alternative_solution()) {
printf("ERROR: %s (error code %d)\n", err.msg, err.err);
wait_for_more_user_input();
}
}
Good luck!
It's not a full solution, but what I tend to do is to have each compilation unit (C file) have unique return codes. It may have a couple of externally visible functions and a bunch of static (only locally visible) functions.
Then within the C file, the return values are unique. Within the C file, if it makes sense, I also decide if I need to log something. Whatever is returned, the caller can know exactly what went wrong.
None of this is great. OTOH exceptions also have wrinkles. When I code in C++ I don't miss C's return handling, but weirdly enough, when I code in C, I can not say with a straight face I miss exceptions. They add complexity in their own way.
My programs may look like this:
some_file.c:
static int _internal_function_one_of_a_bunch(int h)
{
// blah code, blah
if (tragedy_strikes()) {
return 13;
}
// blah more code
return 0; // OK
}
static int _internal_function_another(int h)
{
// blah code, blah
if (tragedy_strikes_again()) {
return 14;
}
if (knob_twitch() != SUPER_GOOD) {
return 15;
}
// blah more code
return 0; // OK
}
// publicly visible
int do_important_stuff(int a)
{
if (flight_status() < NOT_EVEN_OK) {
return 16;
}
return _internal_function_another(a) ||
_internal_function_one_of_a_bunch(2 * a) ||
0; // OK
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
ANSI C equivalent of try/catch?
Is there a way to skip critical code ? More or less like try-catch in modern programming languages. Just now I'm using this technique to spot errors:
bindSignals();
{
signal(SIGFPE, sigint_handler);
// ...
}
int main(void)
{
bindsignals();
int a = 1 / 0; // division by zero, I want to skip it
return 0;
}
The problem is if I don't exit the program in the handler I get the very same error again and again. If possible I would like to avoid goto. I also heard about "longjump" or something. Is it worth to (learn to) use ?
Well, you can probably accomplish something like that using longjmp(), yes.
Possibly with the "help" of some macros. Note the comment on the manual page, though:
longjmp() and siglongjmp() make programs hard to understand and maintain. If possible an alternative should be used.
I'll throw my two cents in on this. C does not have a mechanism like the try/catch that other languages support. You can build something using setjmp() and longjmp() that will be similar, but nothing exactly the same.
Here's a link showing a nice way of using setjmp and longjmp to do what you were thinking; and a code snippet from the same source:
jmp_buf jumper;
int SomeFunction(int a, int b)
{
if (b == 0) // can't divide by 0
longjmp(jumper, -3);
return a / b;
}
void main(void)
{
if (setjmp(jumper) == 0)
{
int Result = SomeFunction(7, 0);
// continue working with Result
}
else
printf("an error occured\n");
}
I'm currently going from C to Java and I'm having a hard time understanding why you'd use try/catch in the first place. With good error checking you should be fine, always always always use the values that are returned from functions, check the errono values, and validate any user input.
I'm done. That's how my code looks like now. Almost like Java and C#.
#include <setjmp.h>
jmp_buf jumper;
#define try if (setjmp(jumper) == 0)
#define catch else
#define skip_to_catch longjmp(jumper, 0)
static void sigint_handler(int sig)
{
skip_to_catch;
}
int main(void)
{
// init error handling once at the beginning
signal(SIGFPE, sigint_handler);
try
{
int a = 1 / 0;
}
catch
{
printf("hello error\n");
}
return 0;
}
I was thinking today about the try/catch blocks existent in another languages. Googled for a while this but with no result. From what I know, there is not such a thing as try/catch in C. However, is there a way to "simulate" them?
Sure, there is assert and other tricks but nothing like try/catch, that also catch the raised exception. Thank you
C itself doesn't support exceptions but you can simulate them to a degree with setjmp and longjmp calls.
static jmp_buf s_jumpBuffer;
void Example() {
if (setjmp(s_jumpBuffer)) {
// The longjmp was executed and returned control here
printf("Exception happened here\n");
} else {
// Normal code execution starts here
Test();
}
}
void Test() {
// Rough equivalent of `throw`
longjmp(s_jumpBuffer, 42);
}
This website has a nice tutorial on how to simulate exceptions with setjmp and longjmp
http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
You use goto in C for similar error handling situations.
That is the closest equivalent of exceptions you can get in C.
Ok, I couldn't resist replying to this. Let me first say I don't think it's a good idea to simulate this in C as it really is a foreign concept to C.
We can use abuse the preprocessor and local stack variables to give use a limited version of C++ try/throw/catch.
Version 1 (local scope throws)
#include <stdbool.h>
#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) {__HadError=true;goto ExitJmp;}
Version 1 is a local throw only (can't leave the function's scope). It does rely on C99's ability to declare variables in code (it should work in C89 if the try is first thing in the function).
This function just makes a local var so it knows if there was an error and uses a goto to jump to the catch block.
For example:
#include <stdio.h>
#include <stdbool.h>
#define try bool __HadError=false;
#define catch(x) ExitJmp:if(__HadError)
#define throw(x) {__HadError=true;goto ExitJmp;}
int main(void)
{
try
{
printf("One\n");
throw();
printf("Two\n");
}
catch(...)
{
printf("Error\n");
}
return 0;
}
This works out to something like:
int main(void)
{
bool HadError=false;
{
printf("One\n");
{
HadError=true;
goto ExitJmp;
}
printf("Two\n");
}
ExitJmp:
if(HadError)
{
printf("Error\n");
}
return 0;
}
Version 2 (scope jumping)
#include <stdbool.h>
#include <setjmp.h>
jmp_buf *g__ActiveBuf;
#define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
#define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
#define throw(x) longjmp(*g__ActiveBuf,1);
Version 2 is a lot more complex but basically works the same way. It uses a
long jump out of the current function to the try block. The try block then
uses an if/else to skip the code block to the catch block which check the local
variable to see if it should catch.
The example expanded again:
jmp_buf *g_ActiveBuf;
int main(void)
{
jmp_buf LocalJmpBuff;
jmp_buf *OldActiveBuf=g_ActiveBuf;
bool WasThrown=false;
g_ActiveBuf=&LocalJmpBuff;
if(setjmp(LocalJmpBuff))
{
WasThrown=true;
}
else
{
printf("One\n");
longjmp(*g_ActiveBuf,1);
printf("Two\n");
}
g_ActiveBuf=OldActiveBuf;
if(WasThrown)
{
printf("Error\n");
}
return 0;
}
This uses a global pointer so the longjmp() knows what try was last run.
We are using abusing the stack so child functions can also have a try/catch block.
Using this code has a number of down sides (but is a fun mental exercise):
It will not free allocated memory as there are no deconstructors being called.
You can't have more than 1 try/catch in a scope (no nesting)
You can't actually throw exceptions or other data like in C++
Not thread safe at all
You are setting up other programmers for failure because they will likely not notice the hack and try using them like C++ try/catch blocks.
In C99, you can use setjmp/longjmp for non-local control flow.
Within a single scope, the generic, structured coding pattern for C in the presence of multiple resource allocations and multiple exits uses goto, like in this example. This is similar to how C++ implements destructor calls of automatic objects under the hood, and if you stick to this diligently, it should allow you for a certain degree of cleanness even in complex functions.
While some of the other answers have covered the simple cases using setjmp and longjmp, in a real application there's two concerns that really matter.
Nesting of try/catch blocks. Using a single global variable for your jmp_buf will make these not work.
Threading. A single global variable for you jmp_buf will cause all kinds of pain in this situation.
The solution to these is to maintain a thread-local stack of jmp_buf that get updated as you go. (I think this is what lua uses internally).
So instead of this (from JaredPar's awesome answer)
static jmp_buf s_jumpBuffer;
void Example() {
if (setjmp(s_jumpBuffer)) {
// The longjmp was executed and returned control here
printf("Exception happened\n");
} else {
// Normal code execution starts here
Test();
}
}
void Test() {
// Rough equivalent of `throw`
longjump(s_jumpBuffer, 42);
}
You'd use something like:
#define MAX_EXCEPTION_DEPTH 10;
struct exception_state {
jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
int current_depth;
};
int try_point(struct exception_state * state) {
if(current_depth==MAX_EXCEPTION_DEPTH) {
abort();
}
int ok = setjmp(state->jumpBuffer[state->current_depth]);
if(ok) {
state->current_depth++;
} else {
//We've had an exception update the stack.
state->current_depth--;
}
return ok;
}
void throw_exception(struct exception_state * state) {
longjump(state->current_depth-1,1);
}
void catch_point(struct exception_state * state) {
state->current_depth--;
}
void end_try_point(struct exception_state * state) {
state->current_depth--;
}
__thread struct exception_state g_exception_state;
void Example() {
if (try_point(&g_exception_state)) {
catch_point(&g_exception_state);
printf("Exception happened\n");
} else {
// Normal code execution starts here
Test();
end_try_point(&g_exception_state);
}
}
void Test() {
// Rough equivalent of `throw`
throw_exception(g_exception_state);
}
Again a more realistic version of this would include some way to store error information into the exception_state, better handling of MAX_EXCEPTION_DEPTH (maybe using realloc to grow the buffer, or something like that).
DISCLAIMER: The above code was written without any testing whatsoever. It is purely so you get an idea of how to structure things. Different systems and different compilers will need to implement the thread local storage differently. The code probably contains both compile errors and logic errors - so while you're free to use it as you choose, TEST it before using it ;)
This is another way to do error handling in C which is more performant than using setjmp/longjmp. Unfortunately, it will not work with MSVC but if using only GCC/Clang is an option, then you might consider it. Specifically, it uses the "label as value" extension, which allows you to take the address of a label, store it in a value and and jump to it unconditionally. I'll present it using an example:
GameEngine *CreateGameEngine(GameEngineParams const *params)
{
/* Declare an error handler variable. This will hold the address
to jump to if an error occurs to cleanup pending resources.
Initialize it to the err label which simply returns an
error value (NULL in this example). The && operator resolves to
the address of the label err */
void *eh = &&err;
/* Try the allocation */
GameEngine *engine = malloc(sizeof *engine);
if (!engine)
goto *eh; /* this is essentially your "throw" */
/* Now make sure that if we throw from this point on, the memory
gets deallocated. As a convention you could name the label "undo_"
followed by the operation to rollback. */
eh = &&undo_malloc;
/* Now carry on with the initialization. */
engine->window = OpenWindow(...);
if (!engine->window)
goto *eh; /* The neat trick about using approach is that you don't
need to remember what "undo" label to go to in code.
Simply go to *eh. */
eh = &&undo_window_open;
/* etc */
/* Everything went well, just return the device. */
return device;
/* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}
If you so wish, you could refactor common code in defines, effectively implementing your own error-handling system.
/* Put at the beginning of a function that may fail. */
#define declthrows void *_eh = &&err
/* Cleans up resources and returns error result. */
#define throw goto *_eh
/* Sets a new undo checkpoint. */
#define undo(label) _eh = &&undo_##label
/* Throws if [condition] evaluates to false. */
#define check(condition) if (!(condition)) throw
/* Throws if [condition] evaluates to false. Then sets a new undo checkpoint. */
#define checkpoint(label, condition) { check(condition); undo(label); }
Then the example becomes
GameEngine *CreateGameEngine(GameEngineParams const *params)
{
declthrows;
/* Try the allocation */
GameEngine *engine = malloc(sizeof *engine);
checkpoint(malloc, engine);
/* Now carry on with the initialization. */
engine->window = OpenWindow(...);
checkpoint(window_open, engine->window);
/* etc */
/* Everything went well, just return the device. */
return device;
/* After the return, insert your cleanup code in reverse order. */
undo_window_open: CloseWindow(engine->window);
undo_malloc: free(engine);
err: return NULL;
}
A quick google search yields kludgey solutions such as this that use setjmp/longjmp as others have mentioned. Nothing as straightforward and elegant as C++/Java's try/catch. I'm rather partial to Ada's exception handling myself.
Check everything with if statements :)
This can be done with setjmp/longjmp in C. P99 has a quite comfortable toolset for this that also is consistent with the new thread model of C11.
In C, you can "emulate" exceptions along with automatic "object reclamation" through manual use of if + goto for explicit error handling.
I often write C code like the following (boiled down to highlight error handling):
#include <assert.h>
typedef int errcode;
errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
errcode ret = 0;
if ( ( ret = foo_init( f ) ) )
goto FAIL;
if ( ( ret = goo_init( g ) ) )
goto FAIL_F;
if ( ( ret = poo_init( p ) ) )
goto FAIL_G;
if ( ( ret = loo_init( l ) ) )
goto FAIL_P;
assert( 0 == ret );
goto END;
/* error handling and return */
/* Note that we finalize in opposite order of initialization because we are unwinding a *STACK* of initialized objects */
FAIL_P:
poo_fini( p );
FAIL_G:
goo_fini( g );
FAIL_F:
foo_fini( f );
FAIL:
assert( 0 != ret );
END:
return ret;
}
This is completely standard ANSI C, separates the error handling away from your mainline code, allows for (manual) stack unwinding of initialized objects much like C++ does, and it is completely obvious what is happening here. Because you are explicitly testing for failure at each point it does make it easier to insert specific logging or error handling at each place an error can occur.
If you don't mind a little macro magic, then you can make this more concise while doing other things like logging errors with stack traces. For example:
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define TRY( X, LABEL ) do { if ( ( X ) ) { fprintf( stderr, "%s:%d: Statement '%s' failed! %d, %s\n", __FILE__, __LINE__, #X, ret, strerror( ret ) ); goto LABEL; } while ( 0 )
typedef int errcode;
errcode init_or_fail( foo *f, goo *g, poo *p, loo *l )
{
errcode ret = 0;
TRY( ret = foo_init( f ), FAIL );
TRY( ret = goo_init( g ), FAIL_F );
TRY( ret = poo_init( p ), FAIL_G );
TRY( ret = loo_init( l ), FAIL_P );
assert( 0 == ret );
goto END;
/* error handling and return */
FAIL_P:
poo_fini( p );
FAIL_G:
goo_fini( g );
FAIL_F:
foo_fini( f );
FAIL:
assert( 0 != ret );
END:
return ret;
}
Of course, this isn't as elegant as C++ exceptions + destructors. For example, nesting multiple error handling stacks within one function this way isn't very clean. Instead, you'd probably want to break those out into self contained sub functions that similarly handle errors, initialize + finalize explicitly like this.
This also only works within a single function and won't keep jumping up the stack unless higher level callers implement similar explicit error handling logic, whereas a C++ exception will just keep jumping up the stack until it finds an appropriate handler. Nor does it allow you to throw an arbitrary type, but instead only an error code.
Systematically coding this way (i.e. - with a single entry and single exit point) also makes it very easy to insert pre and post ("finally") logic that will execute no matter what. You just put your "finally" logic after the END label.
Warning: the following is not very nice but it does the job.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned int id;
char *name;
char *msg;
} error;
#define _printerr(e, s, ...) fprintf(stderr, "\033[1m\033[37m" "%s:%d: " "\033[1m\033[31m" e ":" "\033[1m\033[37m" " ‘%s_error’ " "\033[0m" s "\n", __FILE__, __LINE__, (*__err)->name, ##__VA_ARGS__)
#define printerr(s, ...) _printerr("error", s, ##__VA_ARGS__)
#define printuncaughterr() _printerr("uncaught error", "%s", (*__err)->msg)
#define _errordef(n, _id) \
error* new_##n##_error_msg(char* msg) { \
error* self = malloc(sizeof(error)); \
self->id = _id; \
self->name = #n; \
self->msg = msg; \
return self; \
} \
error* new_##n##_error() { return new_##n##_error_msg(""); }
#define errordef(n) _errordef(n, __COUNTER__ +1)
#define try(try_block, err, err_name, catch_block) { \
error * err_name = NULL; \
error ** __err = & err_name; \
void __try_fn() try_block \
__try_fn(); \
void __catch_fn() { \
if (err_name == NULL) return; \
unsigned int __##err_name##_id = new_##err##_error()->id; \
if (__##err_name##_id != 0 && __##err_name##_id != err_name->id) \
printuncaughterr(); \
else if (__##err_name##_id != 0 || __##err_name##_id != err_name->id) \
catch_block \
} \
__catch_fn(); \
}
#define throw(e) { *__err = e; return; }
_errordef(any, 0)
Usage:
errordef(my_err1)
errordef(my_err2)
try ({
printf("Helloo\n");
throw(new_my_err1_error_msg("hiiiii!"));
printf("This will not be printed!\n");
}, /*catch*/ any, e, {
printf("My lovely error: %s %s\n", e->name, e->msg);
})
printf("\n");
try ({
printf("Helloo\n");
throw(new_my_err2_error_msg("my msg!"));
printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
printerr("%s", e->msg);
})
printf("\n");
try ({
printf("Helloo\n");
throw(new_my_err1_error());
printf("This will not be printed!\n");
}, /*catch*/ my_err2, e, {
printf("Catch %s if you can!\n", e->name);
})
Output:
Helloo
My lovely error: my_err1 hiiiii!
Helloo
/home/naheel/Desktop/aa.c:28: error: ‘my_err2_error’ my msg!
Helloo
/home/naheel/Desktop/aa.c:38: uncaught error: ‘my_err1_error’
Keep on mind that this is using nested functions and __COUNTER__. You'll be on the safe side if you're using gcc.
Redis use goto to simulate try/catch, IMHO it is very clean and elegant:
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success. */
int rdbSave(char *filename) {
char tmpfile[256];
FILE *fp;
rio rdb;
int error = 0;
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
if (!fp) {
redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s",
strerror(errno));
return REDIS_ERR;
}
rioInitWithFile(&rdb,fp);
if (rdbSaveRio(&rdb,&error) == REDIS_ERR) {
errno = error;
goto werr;
}
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp) == EOF) goto werr;
if (fsync(fileno(fp)) == -1) goto werr;
if (fclose(fp) == EOF) goto werr;
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
if (rename(tmpfile,filename) == -1) {
redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
unlink(tmpfile);
return REDIS_ERR;
}
redisLog(REDIS_NOTICE,"DB saved on disk");
server.dirty = 0;
server.lastsave = time(NULL);
server.lastbgsave_status = REDIS_OK;
return REDIS_OK;
werr:
fclose(fp);
unlink(tmpfile);
redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
return REDIS_ERR;
}
If you're using C with Win32, you can leverage its Structured Exception Handling (SEH) to simulate try/catch.
If you're using C in platforms that don't support setjmp() and longjmp(), have a look at this Exception Handling of pjsip library, it does provide its own implementation
After studying the answers given above, I set up a system that automatically handles nested exceptions well. Here is the code I wrote to test my system:
#include "MyOtherTricks.h"
#include "Exceptions.h"
void Testing_InnerMethod();
void Testing_PossibleExceptionThrower();
void TestExceptionHandling()
{
try
{
Testing_InnerMethod();
Say("The inner method exited without an exception.");
}
catch (Exception)
{
Say("I caught an Exception that the inner method did not catch.");
}
end_try
}
void Testing_InnerMethod()
{
try
{
Say("I am in a try block.");
Testing_PossibleExceptionThrower();
Say("The possible exception thrower didn't throw an exception.");
}
catch (ExceptionSubtype1)
Say("I caught an exception, subtype 1.");
catch (ExceptionSubtype2)
{
Say("I caught an exception, subtype 2.");
Say("I will now rethrow it.");
throw(exception);
}
end_try
}
void Testing_PossibleExceptionThrower()
{
Say("Here is the possible exception thrower.");
throw(new(ExceptionSubtype2)); // To further test exception handling, replace ExceptionSubtype2 in this line with Exception or ExceptionSubtype1, or comment out this line entirely.
Say("No, I won't throw an exception!");
}
The example code relies on two files, Exceptions.h and Exceptions.c. Here is Exceptions.h:
#include <setjmp.h>
extern jmp_buf* Exception_Handler;
#define try do \
{ \
jmp_buf* outerExceptionHandler = Exception_Handler; \
jmp_buf exceptionHandler; \
Exception_Handler = &exceptionHandler; \
Exception exception = (Exception)setjmp(exceptionHandler); \
if (exception != 0) Exception_Handler = outerExceptionHandler; \
if (exception == 0) \
{ \
// The try block goes here. It must not include a return statement or anything else that exits the try...end_try block, because then the outer exception handler will not be restored.
#define catch(exceptionType) Exception_Handler = outerExceptionHandler; \
} \
else if (Object_IsSomeTypeOf(exception, exceptionType)) \
{
// The catch block goes here. It may include a return statement or anything else that exits the try...end_try block. A break statement will exit only the try...end_try block.
#define end_try } \
else \
throw(exception); \
} while(0);
void throw(Exception exception);
And here is Exceptions.c:
#include "MyOtherTricks.h"
#include "Exceptions.h"
jmp_buf* Exception_Handler = 0;
void throw(Exception exception)
{
if (Exception_Handler == 0) FailBecause("Uncaught exception.");
longjmp(*Exception_Handler, (int)exception);
}
Note that this code references some additional methods that I'm not including here (because class inheritance in C is off-topic). To make this code work for you, you'll have to understand this code well enough to replace a few things. In particular, if you want to distinguish between different types of exceptions, you'll need to realize that this code assumes that Object_IsSomeTypeOf(new(ExceptionSubtype1), Exception) returns true and Object_IsSomeTypeOf(new(ExceptionSubtype1), ExceptionSubtype2) returns false, and you'll need to either make your own version of my Object_IsSomeTypeOf macro or replace it with something else.
Perhaps not a major language (unfortunately), but in APL, theres the ⎕EA operation (stand for Execute Alternate).
Usage:
'Y' ⎕EA 'X'
where X and Y are either code snippets supplied as strings or function names.
If X runs into an error, Y (usually error-handling) will be executed instead.
I am confused by the for(;;) construct. I think it is a form of shorthand for an unlimited for loop but I can't be sure.
Here is the code:
for(;;)
{
//whatever statements
}
Your guess is correct; it's an infinite loop.* This is a common C idiom, although many people (including me) believe the following to be less cryptic:
while (1) { whatever statements; }
* It's infinite assuming there are no break/return/etc. statements inside the loop body.
It's an un-terminated loop. It is sometimes written with a while:
while (1)
or even better:
while (true)
I would expect to see a break or return inside any such loop, no matter whether it is written with for or while. There has to be some abnormal control flow or it really will be an infinite loop.
Yes, that's the for C syntax with blank fields for initialization expression, loop condition and increment expression.
The for statement can also use more than one value, like this sample :
for (i=0, j=100, k=1000; j < 500 || i<50 || k==5000; i++, j+=2, k*=6) {};
Maybe one step beyond in for understanding ? =)
Yes, the expressions in the for loop are just optional. if you omit them, you will get an infinite loop. The way to get out is break or exit or so.
This statement is basically equal to:
while(1) {}
There is no start, no condition and no step statement.
As I understand it, for(;;) creates a deliberate non-exiting loop. Your code is expected to exit the loop based on one or more conditions. It was once provided to me as a purer way to have a do while false loop, which was not considered good syntax. Based on the exit condition, it is easier to dispatch to a function to handle the result, failure, warning, or success, for example.
My explanation may not be the reason someone used that construct, but I'll explain in greater detail what it means to me. This construct may be someone's "Pure C" way of having a loop in which you can serially perform multiple steps, whose completion mean something like your application has performed all steps of initialization.
#define GEN_FAILURE -99
#define SUCCESS 0
/* perform_init_step1() and perform_init_step2() are dummy
place-holder functions that provide a complete example.
You could at least have one of them return non-zero
for testing. */
int perform_init_step1();
int perform_init_step2();
int perform_init_step1()
{
return 0;
}
int perform_init_step2()
{
return 0;
}
int ret_code = GEN_FAILURE;
for(;;)
{
if(SUCCESS != perform_init_step1())
{
ret_code = -1;
break;
}
if(SUCCESS != perform_init_step2())
{
ret_code = -2;
break;
}
break;
}
If part of the initialization fails, the loop bails out with a specific error code.
I arrived at using C having done a lot of firmware work, writing in assembly language. Good assembly language programmers taught me to have a single entry point and single exit. I took their advice to heart, because their creed helped them and me immensely when debugging.
Personally, I never liked the for(;;) construct, because you can have an infinite loop if you forget to break; out at the end.
Someone I worked with came up with do..until(FALSE), but the amount of proper C furvor this caused was not to be believed.
#define GEN_FAILURE -99
#define SUCCESS 0
/* perform_init_step1() and perform_init_step2() are dummy
place-holder functions that provide a complete example.
You could at least have one of them return non-zero
for testing. */
int perform_init_step1();
int perform_init_step2();
int perform_init_step1()
{
return 0;
}
int perform_init_step2()
{
return 0;
}
int ret_code = GEN_FAILURE;
do
{
if(SUCCESS != perform_init_step1())
{
ret_code = -1;
break;
}
if(SUCCESS != perform_init_step2())
{
ret_code = -2;
break;
}
}
until (FALSE);
This runs once, no matter what.