What are some good ways of handling errors (cleanup and abort) in a function that initializes multiple resources in C?

前端 未结 2 485
执念已碎
执念已碎 2021-01-20 01:22

First of all, if someone can reword the question to make it clearer, please do.

A common occurrence in C programming is having several resources to be initialized/al

2条回答
  •  甜味超标
    2021-01-20 01:56

    4: Global variables, woohoo!!! Because everybody loves global variables, just like they love goto. But seriously, if you limit the scope of the variables to file scope (using the static keyword) then it's not that bad. Side note: the cleanup function takes/returns the error code unchanged, so as to declutter the code in the init_function.

    static A *a = NULL;
    static B *b = NULL;
    static C *c = NULL;
    
    uint32_t cleanup( uint32_t errcode )
    {
        if ( c )
            dealloc_c(&c);
        if ( b )
            dealloc_b(&b);
        if ( a )
            dealloc_a(&a);
    
        return errcode;
    }
    
    uint32_t init_function( void )
    {
        if ( alloc_a(&a) != SUCCESS )
            return cleanup(INIT_FAIL_A);
    
        if ( alloc_b(&b) != SUCCESS )
            return cleanup(INIT_FAIL_B);
    
        if ( alloc_c(&c) != SUCCESS )
            return cleanup(INIT_FAIL_C);
    
        profit(a,b,c);
        return INIT_SUCCESS;
    }
    

    5: Faux OOP. For those who can't handle the truth (that global variables are actually useful in C programs), you can take the C++ approach. C++ takes all of the global variables, puts them into a structure, and calls them "member" variables. And somehow that makes everybody happy.

    The trick is to pass a pointer to the structure to all of the functions, as the first argument. C++ does this behind the scenes, in C you have to do it explicitly. I call the pointer that so as to avoid conflicts/confusion with this.

    // define a class (uhm, struct) with status, a cleanup method, and other stuff as needed
    typedef struct stResources
    {
        char *status;
        A *a;
        B *b;
        C *c;
        void (*cleanup)(struct stResources *that);
    }
    stResources;
    
    // the cleanup method frees all resources, and frees the struct
    void cleanup( stResources *that )
    {
        if ( that->c )
            dealloc_c( &that->c );
        if ( that->b )
            dealloc_b( &that->b );
        if ( that->a )
            dealloc_a( &that->a );
    
        free( that );
    }
    
    // the init function returns a pointer to the struct, or NULL if the calloc fails
    // the status member variable indicates whether initialization succeeded, NULL is success
    stResources *init_function( void )
    {
        stResources *that = calloc( 1, sizeof(stResources) );
    
        if ( !that )
            return NULL;
    
        that->cleanup = cleanup;
    
        that->status = "Item A is out to lunch";
        if ( alloc_a( &that->a ) != SUCCESS )
            return that;
    
        that->status = "Item B is never available when you need it";
        if ( alloc_b( &that->b ) != SUCCESS )
            return that;
    
        that->status = "Item C is being hogged by some other process";
        if ( alloc_c( &that->c ) != SUCCESS )
            return that;
    
        that->status = NULL;  // NULL is success
        return that;
    }
    
    int main( void )
    {
        // create the resources
        stResources *resources = init_function();
    
        // use the resources
        if ( !resources )
            printf( "Buy more memory already\n" );
        else if ( resources->status != NULL )
            printf( "Uhm yeah, so here's the deal: %s\n", resources->status );
        else
            profit( resources->a, resources->b, resources->c );
    
        // free the resources
        if ( resources )
            resources->cleanup( resources );
    }
    

提交回复
热议问题