是否有可能在纯C中实现RAII?
我认为不可能以任何理智的方式,但也许可能使用某种肮脏的技巧.重载标准free
函数会想到或者可能覆盖堆栈上的返回地址,这样当函数返回时,它会调用一些其他函数以某种方式释放资源?或者也许有一些setjmp/longjmp技巧?
这纯粹是学术上的兴趣,我无意写出这种不可移植和疯狂的代码,但我想知道这是否可能.
这是固有的实现依赖,因为标准不包括这种可能性.对于GCC,cleanup
当变量超出范围时,该属性会运行一个函数:
#includevoid scoped(int * pvariable) { printf("variable (%d) goes out of scope\n", *pvariable); } int main(void) { printf("before scope\n"); { int watched __attribute__((cleanup (scoped))); watched = 42; } printf("after scope\n"); }
打印:
before scope variable (42) goes out of scope after scope
看到这里
将RAII引入C(当你没有cleanup()
)的一个解决方案是使用将执行清理的代码包装你的函数调用.这也可以打包在一个整洁的宏(最后显示).
/* Publicly known method */ void SomeFunction() { /* Create raii object, which holds records of object pointers and a destruction method for that object (or null if not needed). */ Raii raii; RaiiCreate(&raii); /* Call function implementation */ SomeFunctionImpl(&raii); /* This method calls the destruction code for each object. */ RaiiDestroyAll(&raii); } /* Hidden method that carries out implementation. */ void SomeFunctionImpl(Raii *raii) { MyStruct *object; MyStruct *eventually_destroyed_object; int *pretend_value; /* Create a MyStruct object, passing the destruction method for MyStruct objects. */ object = RaiiAdd(raii, MyStructCreate(), MyStructDestroy); /* Create a MyStruct object (adding it to raii), which will later be removed before returning. */ eventually_destroyed_object = RaiiAdd(raii, MyStructCreate(), MyStructDestroy); /* Create an int, passing a null destruction method. */ pretend_value = RaiiAdd(raii, malloc(sizeof(int)), 0); /* ... implementation ... */ /* Destroy object (calling destruction method). */ RaiiDestroy(raii, eventually_destroyed_object); /* or ... */ RaiiForgetAbout(raii, eventually_destroyed_object); }
您可以SomeFunction
使用宏来表示所有锅炉板代码,因为每次调用都是相同的.
例如:
/* Declares Matrix * MatrixMultiply(Matrix * first, Matrix * second, Network * network) */ RTN_RAII(Matrix *, MatrixMultiply, Matrix *, first, Matrix *, second, Network *, network, { Processor *processor = RaiiAdd(raii, ProcessorCreate(), ProcessorDestroy); Matrix *result = MatrixCreate(); processor->multiply(result, first, second); return processor; }); void SomeOtherCode(...) { /* ... */ Matrix * result = MatrixMultiply(first, second, network); /* ... */ }
注意:你想要使用像P99这样的高级宏框架来制作类似上面的东西.
如果您的编译器支持C99(或者甚至是其中很大一部分),您可以使用可变长度数组(VLA),例如:
int f(int x) { int vla[x]; // ... }
如果内存服务,gcc在添加到C99之前已经/支持了这个功能.这(大致)相当于以下简单情况:
int f(int x) { int *vla=malloc(sizeof(int) *x); /* ... */ free vla; }
但是,它不允许您执行dtor可以执行的任何其他操作,例如关闭文件,数据库连接等.