Implement RAII in pure C?

Is it possible to implement RAII in pure C?

I suppose this is not possible in common sense, but perhaps possibly using some kind of dirty trick. Overloading the standard free function comes to mind, or perhaps rewrites the return address on the stack, so when the function returns, does it call some other function that somehow frees up resources? Or maybe with the setjmp / longjmp trick?

This is purely academic interest, and I'm not going to write such non-portable and crazy code, but I wonder if this is possible at all.

+52
c raii
Dec 15 '08 at 13:40
source share
12 answers

This is implementation dependent as the standard does not include this capability. For GCC, the cleanup attribute performs a function when the 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"); } 

Print

 before scope variable (42) goes out of scope after scope 

Look here

+68
Dec 15 '08 at 15:38
source share

One solution for translating RAII to C (when you don't have cleanup() ) is to enclose the function call in code that will do the cleanup. It can also be packaged in a neat 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 boiler panel code in SomeFunction using macros, because 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 would like to use an extended macro structure such as P99 to make something like the above described possible.

+10
Jun 07 '13 at 21:43
source share

If your compiler supports C99 (or even a significant part of it), you can use a variable-length array (VLA), for example:

 int f(int x) { int vla[x]; // ... } 

If memory is in use, gcc has / has supported this feature long before it was added to C99. This is (roughly) equivalent to the simple case:

 int f(int x) { int *vla=malloc(sizeof(int) *x); /* ... */ free vla; } 

However, it does not allow you to do any other actions that dtor can perform, for example, close files, connect to databases, etc.

+8
Jul 08 '10 at 19:55
source share

Probably the easiest way is to use goto to jump to the label at the end of the function, but it's probably too manual for what you're looking for.

+3
Dec 15 '08 at 15:20
source share

I would choose to rewrite the return address on the stack. That would be the most transparent. Replacing free will only work with a bunch of "objects."

+1
Dec 15 '08 at 14:02
source share

Have you looked at alloca ()? It will free itself when var leaves the scope. But in order to use it effectively, the caller must always do alloca before sending it to things ... If you did strdup, well, you cannot use alloca.

+1
Apr 24 '09 at 7:02
source share

Hey, you're trying to recreate CFront !

0
Dec 15 '08 at 13:45
source share

Check out https://github.com/psevon/exceptions-and-raii-in-c to implement C unique and common smart pointers and exceptions. This implementation is based on the BEGIN ... END macros, replacing curly braces and discovering that smart controllers are out of scope, as well as macro returns.

0
Apr 7 '14 at 19:55
source share

I did not know about clearing attributes before. Of course, this is a clear solution in which it is applicable, but it seems to behave badly with exception implementations based on setjmp / longjmp; the cleanup function is not called for any intermediate areas / functions between the scope that throws the exception and the scope that catches it. Alloca does not have this problem, but using alloca, you cannot transfer ownership of a piece of memory to an external area from the function that called it, because the memory is allocated from the stack frame. It is possible to implement smart controllers, somewhat similar to C ++ unique_ptr and shared_ptr, considering that for this it is necessary to use macro brackets instead of {} and return to be able to associate additional logic with the input / output of the area. See Autocleanup.c at https://github.com/psevon/exceptions-and-raii-in-c for implementation.

0
Apr 08 '14 at 17:11
source share

In addition to this part of Johannes answer:

the cleanup attribute runs the function when the variable goes out of scope

There is a restriction on the cleanup attribute ( http://gcc.gnu.org/onlinedocs/gcc-4.0.4/gcc/Variable-Attributes.html ): This attribute can only be applied to scope variables of an automatic function.

So, if there is a static variable in the file, you can implement RAII for the static variable as follows:

 #include <stdio.h> #include <stdlib.h> static char* watched2; __attribute__((constructor)) static void init_static_vars() { printf("variable (%p) is initialazed, initial value (%p)\n", &watched2, watched2); watched2=malloc(1024); } __attribute__((destructor)) static void destroy_static_vars() { printf("variable (%p), value( %p) goes out of scope\n", &watched2, watched2); free(watched2); } int main(void) { printf("exit from main, variable (%p) value(%p) is static\n", &watched2, watched2); return 0; } 

This is a test:

 >./example variable (0x600aa0) is initialazed, initial value ((nil)) exit from main, variable (0x600aa0) value(0x16df010) is static variable (0x600aa0), value( 0x16df010) goes out of scope 
0
Apr 25 '14 at 10:17
source share

I am implementing aC # using the raii style for c in pure c and some asm strings, I will publish src on github soon

0
Apr 26 '19 at 12:22
source share
 my implementation of raii for c in pure c and minimal asm @ https://github.com/smartmaster/sml_clang_raii **RAII for C language in pure C and ASM** **featurs : ** -easy and graceful to use - no need seperate free cleanup functions - able to cleanup any resources or call any function on scope exits **User guide : ** -add source files in src folder to your project -include sml_raii_clang.h in.c file -annote resource and its cleanup functions 

/ * sample code * /

 void sml_raii_clang_test() { //start a scope, the scope name can be any string SML_RAII_BLOCK_START(0); SML_RAII_VOLATILE(WCHAR*) resA000 = calloc(128, sizeof(WCHAR)); //allocate memory resource SML_RAII_START(0, resA000); //indicate starting a cleanup code fragment, here 'resA000' can be any string you want if (resA000) //cleanup code fragment { free(resA000); resA000 = NULL; } SML_RAII_END(0, resA000); //indicate end of a cleanup code fragment //another resource ////////////////////////////////////////////////////////////////////////// SML_RAII_VOLATILE(WCHAR*) res8000 = calloc(128, sizeof(WCHAR)); SML_RAII_START(0, D000); if (res8000) { free(res8000); res8000 = NULL; } SML_RAII_END(0, D000); //scope ended, will call all annoated cleanups SML_RAII_BLOCK_END(0); SML_RAII_LABEL(0, resA000); //if code is optimized, we have to put labels after SML_RAII_BLOCK_END SML_RAII_LABEL(0, D000); } 
0
Apr 28 '19 at 16:35
source share



All Articles