RAII-like Error Handling and Resource Management in C

Error handling and resource management are pervasive in programs. RAII originated in C++ is great. With RAII, it is much easier to write easy-to-read code that allocats/deallocats resources in the constructor/destructors. By representing a resource with a local object, we are sure that local object’s destructor will release the resource and will not forget to release the resource even there are exceptions. One example is as follows.

void foo() {
    // a and b can acquire resources, such as lock, file
    // or memory.
    // They can also throw exceptions for error handling.
    A a();
    if (!a.ok())
       return;

    B b();
    if (!b.ok())
       return;

    // do something here

    // resources will be deallocated automatically in the 
    // reverse order b -> a for objects that have been
    // constructed: 
    return;
}

In C, can we achieve similar semantics with some code idiom that are easy to write and read?

An idiom in C may achieve the similar goal, which I come across here. Here, I wrote the code which does the similar thing as the code in C++ following RAII above.

void foo()
{
    /* acquire resources */
    A *a = acquireA();
    if ( !a )
        goto exit;

    B *b = acquireB();
    if ( !b )
        goto cleanupA;

    /* do something here */

    /* release resources */
cleanupB:
    releaseB(b);
cleanupA:
    releaseA(a);
exit:
    return;
}

The code is not as clear or simple as the C++ code but can do what we want correctly. Yes, goto is used. However, goto is not an evil if it is used correctly. For this situation, goto actually makes the code clearer. Try to write a version of the code that does not use goto and scale it to process more resources like 3 or 4 acquireA-like functions.

On the other hand, GCC supports the cleanup variable attribute as an non-standard extension for supporting RAII:

cleanup (cleanup_function) The cleanup attribute runs a function when the variable goes out of scope. This attribute can only be applied to auto function scope variables; it may not be applied to parameters or variables with static storage duration. The function must take one parameter, a pointer to a type compatible with the variable. The return value of the function (if any) is ignored.

If -fexceptions is enabled, then cleanup_function is run during the stack unwinding that happens during the processing of the exception. Note that the cleanup attribute does not allow the exception to be caught, only to perform an action. It is undefined what happens if cleanup_function does not return normally.

To achieve the similar semantics above, the C code with cleanup can be as follows.

void foo()
{
    __attribute__((cleanup(releaseA))) A *a = acquireA();
    if ( !a )
        return;

    __attribute__((cleanup(releaseB))) B *b = acquireB();
    if ( !b )
        return;

    /* do something here */

    /* cleanup (works as): */
    /* if b is allocated, call releaseB() */
    /* if a is allocated, call releaseA() */
    return;
}

The code is clearer but requires the GCC extension. And the cleanup_function should be carefully written to return normally. Otherwise, the behavior is undefined.

To see how the cleanup works, check this example code as follows, compile it with a modern (tested on gcc 4.8.2) version of gcc and run it. The acquire{A,B} functions randomly decide whether the resource allocation “succeed”. Run it multiple times and see what happens.

// An illustration of the cleanup attribute:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

typedef int A;
typedef int B;

A g_a;

A* acquireA()
{
    printf("acquireA\n");
    int r = rand();
    if (r % 2 == 0) {
        return NULL;
    } else {
        return &g_a;
    }
}

B* acquireB()
{
    printf("acquireB\n");
    int r = rand();
    if (r % 2 == 0) {
        return NULL;
    } else {
        return &g_a;
    }

}

void releaseA()
{
    printf("releaseA\n");
}

void releaseB()
{
    printf("releaseB\n");
}

void foo()
{
    __attribute__((cleanup(releaseA))) A *a = acquireA();
    if ( !a )
        return;

    __attribute__((cleanup(releaseB))) B *b = acquireB();
    if ( !b )
        return;

    printf("foo()\n");

    return;
}

int main(int argc, const char *argv[])
{
    srand(time(NULL));
    foo(); 
    return 0;
}

Similar Posts

  • 5 necessary PC hardware that a programmer needs to upgrade

    The world of technology has evolved drastically over the last few decades. Almost every aspect of our lives is dominated by technology. At the heart of technology lies computer programming. Computer programming is what dictates the success of the technology. I mean think about it, your smartphone, PC, ETC they all rely on programming. This…

  • |

    Extracting EC Public Keys with OpenSSL

    In the realm of cryptography, handling and managing keys is a crucial task. The command provided is a series of operations using OpenSSL and other command-line utilities to extract and format an elliptic curve (EC) public key. Let’s break down the command to understand its purpose and functionality. The Command Extracting EC Public Keys with…

  • Micosoft招聘部分算法题

    Micosoft招聘部分算法题 1.链表和数组的区别在哪里? 2.编写实现链表排序的一种算法。说明为什么你会选择用这样的方法? 3.编写实现数组排序的一种算法。说明为什么你会选择用这样的方法? 4.请编写能直接实现strstr()函数功能的代码。 5.编写反转字符串的程序,要求优化速度、优化空间。 6.在链表里如何发现循环链接? 7.给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。 8.写一个函数,检查字符是否是整数,如果是,返回其整数值。(或者:怎样只用4行代码编写出一个从字符串到长整形的函数?) 9.给出一个函数来输出一个字符串的所有排列。 10.请编写实现malloc()内存分配函数功能一样的代码。 11.给出一个函数来复制两个字符串A和B。字符串A的后几个字节和字符串B的前几个字节重叠。 12.怎样编写一个程序,把一个有序整数数组放到二叉树中? 13.怎样从顶部开始逐层打印二叉树结点数据?请编程。 14.怎样把一个链表掉个顺序(也就是反序,注意链表的边界条件并考虑空链表)? 来源:·日月光华 bbs.fudan.edu.cn Read more: The C Programming Style that I Follow fclose – Close a Stream Hashing Library for C Cheatsheet: Git Branching with a Git Server Creating a Child Process using posix_spawn in C in Linux How to Statically…

  • Crypto Options Explained: A Guide to Calls, Puts, and Premiums

    Crypto options are a powerful and flexible type of financial derivative that gives a trader the right, but not the obligation, to buy or sell a specific cryptocurrency at a predetermined price on or before a certain date. [1][2] This key feature—the option to walk away—distinguishes them from futures contracts and makes them a versatile…

Leave a Reply

Your email address will not be published. Required fields are marked *