2

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:

  1. Encapsulate "sub initializations" in functions (e.g. semaphore creation could be a function create_semaphore())

  2. 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;
    
  3. 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"?

Community
  • 1
  • 1
exilit
  • 1,156
  • 11
  • 23
  • 3
    use the `goto` blocks, if you look at your semaphore logic, you've got cumulatively repeating lines which would like up perfectly as consecutive lines with labels such as `_err1`, `_err2`, `_err3`. Every time you have to repeat/copy+paste these lines is a chance to make an error. Check this stackoverflow question [valid-use-of-goto-for-error-management-in-c](http://stackoverflow.com/questions/788903/valid-use-of-goto-for-error-management-in-c) – amdixon Feb 27 '14 at 11:17
  • Well `goto` sounds good. But if I do that I will have to do this very often because there are many situations with a similar logic. Wouldn't that produce very bad-styled code? – exilit Feb 27 '14 at 11:22
  • if the logic is similar enough can you abstract it with a macro or a function, to remove the repetition? – amdixon Feb 27 '14 at 11:27
  • 2
    well, every keyword has its proper context. there is a reason `goto` is disliked; seems mostly because it makes the code harder to read+maintain. in this case i would say this coding style will improve your source, so dont think it qualifies for the spirit of disapproval of `goto` in general – amdixon Feb 27 '14 at 11:31
  • Thank you for spotting that. +1 for that. Just found a [blog post](http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c/) saying the same. Would like to hear more opinions on that. (didn't see the link you posted, sorry and thanks). – exilit Feb 27 '14 at 11:38
  • 1
    Use of `goto` isn't all bad in this case. The actual `goto` keyword in `c` is relatively safe. Use of `longjmp` is where things become quite problematic. The problem you are posing is quite common. C++ introduced stack unwinding to deal with it. – Eric Urban Feb 28 '14 at 02:48

0 Answers0