raii c_C中类似RAII的错误处理和资源管理

raii 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.

错误处理和资源管理普遍存在于程序中。 起源于C ++的RAII很棒。 使用RAII,编写易于阅读的代码可以轻松地在构造函数/析构函数中分配/分配资源。 通过用本地对象表示资源,我们可以确保本地对象的析构函数将释放资源,并且即使有例外,也不会忘记释放资源。 一个例子如下。

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?

在C语言中,我们是否可以通过一些易于编写和阅读的代码惯用法来实现类似的语义?

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.

C语言中的一个习惯用法可以达到类似的目标,我在这里遇到了这个问题 。 在这里,我按照上面的RAII编写了与C ++中的代码具有相似功能的代码。

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.

该代码不像C ++代码那么清晰或简单,但是可以正确执行我们想要的操作。 是的,使用了goto 。 但是,正确地使用goto并不是有害的。 对于这种情况, goto实际上使代码更清晰。 尝试编写不使用goto的代码版本,并对其进行缩放以处理更多资源,例如3个或4个类A的函数。

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

另一方面,GCC支持将cleanup variable属性作为支持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.

cleanup(cleanup_function)当变量超出范围时,cleanup属性将运行一个函数。 此属性只能应用于自动功能范围变量; 它可能不适用于具有静态存储持续时间的参数或变量。 该函数必须带有一个参数,即指向与该变量兼容的类型的指针。 该函数的返回值(如果有)将被忽略。

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.

如果启用了-fexceptions,则cleanup_function在异常处理期间发生的堆栈展开期间运行。 请注意,cleanup属性不允许捕获异常,而只能执行操作。 如果cleanup_function无法正常返回,将会发生什么仍未定义。

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

为了实现上面类似的语义,带有cleanup的C代码可以如下。

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.

代码更清晰,但需要GCC扩展名。 并且应该仔细编写cleanup_function以使其正常返回。 否则,行为是不确定的。

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.

要查看cleanup工作方式,请按以下步骤检查此示例代码,并使用现代版本(在gcc 4.8.2上测试)的gcc进行编译并运行它。 acquire{A,B}函数随机决定资源分配是否“成功”。 多次运行,看看会发生什么。

// 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;
}

翻译自: https://www.systutorials.com/raii-like-error-handling-and-resource-management-in-c/

raii c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值