I couldn't find anything similar to my Question. I hope there are some advices out there.
I noticed in the last time that I cannot find any readable and well structured way to deal with allocation errors and other initialization problems. The main problem is that during initialization there are many possible error paths and depending on where the error occurred you have to undo the changes made so far.
If you look at a simple example dealing with semaphores:
Any_Struct dosomething(void){
Any_Struct *a_struct;
sem_t *semaphore;
semaphore = malloc(sizeof(*semaphore));
if(semaphore == NULL){
return NULL;
}
if(sem_init(semaphore, 0, 0) == -1){
free(semaphore);
return NULL;
}
a_struct = malloc(sizeof(*a_struct));
if(a_struct == NULL){
sem_destroy(semaphore);
free(semaphore);
return NULL;
}
a_struct->sem = semaphore;
return a_struct;
}
As you can see this way has the big disadvantage to grow very fast. Each dynamic object i need produces a very large overhead for error handling. Moreover this is very error prone because it is very likely to forget a free()
after adding new objects.
There are three solutions to this I can think of:
Encapsulate "sub initializations" in functions (e.g. semaphore creation could be a function
create_semaphore()
)First allocate all memory (which does not depend on each other) and if anything went wrong free one after the other:
/* ..... */ semaphore = malloc(sizeof(*semaphore)); a_struct = malloc(sizeof(*a_struct)); if(semaphore == NULL || a_struct == NULL){ sem_destroy(semaphore); free(semaphore); return NULL; } if(sem_init(semaphore, 0, 0)){ free(a_struct); free(semaphore); return NULL; } return a_struct;
Use goto ( I know, I am not allowed to do that)
But all the mentioned "solutions" have their own disadvantages and do not satisfy me.
So are there any better options or maybe best practices? Or would you say: "just stop wracking your brain abut such a stupid thing"?