尽管有许多的争议,但我还是觉得 C++ 中的 RAII 惯用法是个好东西,也是写 C 代码时唯一怀念的 C++ 特性。下面是一些 C 语言实现 RAII 的方法:

gcc

GCC 上可以使用​​cleanup 扩展实现​

#define RAII_VARIABLE(vartype,varname,initval,dtor) \
void _dtor_ ## varname (vartype * v) { dtor(*v); } \
vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)

void example_usage() {
RAII_VARIABLE(FILE*, logfile, fopen("logfile.txt", "w+"), fclose);
fputs("hello logfile!", logfile);
}

Windows

Windows 下可以使用SEH的​​__try/__finally​​,话说 Windows 下其实有不少东西还是挺方便的。

标准 C

为了更好的性能和可移植性,还可以使用​​setjmp/longjmp​​​机制,一个简单封装如下,使用​​TRY/FINALLY​​即可

#define TRY do{ jmp_buf ex_buf__; switch( setjmp(ex_buf__) ){ case 0: while(1){
#define CATCH(x) break; case x:
#define FINALLY break; } default:
#define ETRY } }while(0)
#define THROW(x) longjmp(ex_buf__, x)

上面的代码里在​​switch​​​里使用了​​while​​​,使用的是Duff Device技术(如下所示)。这都是因为C语言中不允许​​goto​​​到​​switch​​​语句的任何​​case​​处。

Duff Device

​C-FAQ​​中给出形式是:

int n = (count + 7) / 8;   /* count > 0 assumed */
switch (count % 8)
{
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}

这在C语言中是合法的,​​switch​​​语句的唯一作用就是”陷落”到开始的合适位置开始执行。上例中这样一次比较可以执行8次拷贝操作,同时也不用关心​​count​​​不能被8整除等问题。这种操作是一种特殊的循环展开机制(loop-unrolling mechanism)。(​​switch​​​语句中的​​case​​实际上就是标签,这就容易理解了)。

这种技术在特别低层的代码(如驱动)或特定需求(如C语言实现RAII)时可以使用。


--------------------- 

作者:Virbox 技术博客

版权声明:本文为博主原创文章,转载请附上博文链接!