Implementing RAII in pure C?
If your compiler supports C99 (or even a substantial part of it) you can use a variable length array (VLA), such as:
int f(int x) {
int vla[x];
// ...
}
If memory serves, gcc had/supported this feature well before it was added to C99. This is (roughly) equivalent to the simple case of:
int f(int x) {
int *vla=malloc(sizeof(int) *x);
/* ... */
free vla;
}
It does not, however, let you do any of the other things a dtor can do such as closing files, database connections, etc.
This is inherent implementation dependent, since the Standard doesn't include such a possibility. For GCC, the cleanup
attribute runs a function when a variable goes out of scope:
#include <stdio.h>
void 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");
}
Prints:
before scope
variable (42) goes out of scope
after scope
See here
Probably the easiest way is to use goto to jump to a label at the end of a function but that's probably too manual for the sort of thing you're looking at.
One solution to bring RAII to C (when you don't have cleanup()
) is to wrap your function call with code that will perform a cleanup. This can also be packaged in a tidy macro (shown at the end).
/* 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);
}
You can express all of the boiler plate code in SomeFunction
with macros since it will be the same for every call.
For example:
/* 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);
/* ... */
}
Note: you'd want to make use of an advanced macro framework such as P99 to make something like the above possible.