NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
Braiding the spaghetti: implementing defer in the preprocessor (gustedt.wordpress.com)
pif 104 days ago [-]
If you want to use RAII in C, just use the "cleanup" attribute of gcc.

https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attribute...

wffurr 104 days ago [-]
*in non portable non standard C
gobblegobble2 104 days ago [-]
Supported by both gcc and clang, so portable enough for most people. I also like glib's cleanup attribute wrappers: https://docs.gtk.org/glib/auto-cleanup.html.
wyldfire 104 days ago [-]
It is non portable, that's true. But defer [1] is at least undergoing discussions w/the standard committee. Didn't make it into C23 but maybe next time.

[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2895.htm

gustedt 104 days ago [-]
That's not very practical if you have several operations to perform, you'd have to create a specialized function each time. So no, that extension is not an alternative to designing a new flow control feature. (And it is not new, other languages have it already.)
fuhsnn 103 days ago [-]
>I also have some ideas how to make this defer implementation work with break and continue, but that is unfortunately a bit more nasty.

This is the kind of things better done in the compiler, I implemented the n3199 variant of defer[1], along with [[gnu::cleanup]], in a small C compiler with about 200 LOC by extending the VLA de-allocation algorithm, the process is archived at [2].

[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3199.htm [2] https://github.com/fuhsnn/slimcc-defer/commits/

gustedt 103 days ago [-]
Thanks for the pointer! Definitively, on the long run these things should go into compilers directly, yes.

BTW, the break continue stuff now works in the development branch.

I notice that in that implementation there is an addition to the compiler state with indirections, whereas in ellipsis all is done with just three local variables; two per-function for knowing if we have to unwind completely and for the return value, and one per loop construct to know if we have to unwind there. All the rest is static information about nestedness and lexicographic ordering of code that can be deduced very early (here in the preprocessor). The result of all of this is a very organized braid of gotos, that in general are mostly optimized out.

andrewla 104 days ago [-]
I love the idea of having a defer statement in general for C, but given the complexity of C scopes it's a little hard to wrangle (switch statements in particular break a lot of assumptions about how scopes should work).

I would prefer a directly scoped syntax similar for a for statement, something like

   defer (void * p = malloc(17); free(p)) {
     ...
   }
This gets more cumbersome as you have more such scopes in a function, but it gives a sane bounding. You can sort of do this now with a properly constructed for loop so that it cleans up on regular exit from the loop, but it can't handle exception exits (returns, breaks, and god forbid goto or longjmp).
gustedt 104 days ago [-]
This would be much more restrictive that what is proposed, because usually you would add `defer` statements as you go in a block, when you allocate more resources for example.

Also usually you should be able to have several actions,

  defer { 
    one; 
    two; 
  }
And the bounding in the proposed feature is sane, I think, it is the surrounding compound statement (AKA `{ ... }` block).
aedrax 104 days ago [-]
shameless plug for my defer header: https://github.com/aedrax/defer.h
sirwhinesalot 104 days ago [-]
You can implement an acceptable defer with the standard preprocessor and some switch abuse.

The only annoying part is needing to use "defer_return" and such instead of the proper keywords.

Unlike most defer implementations for C this doesn't need a function-scope fixed sized block, it's all properly scoped, the switch effectively models a state machine. Similar tricks can be used to implement yield and such.

teo_zero 104 days ago [-]
How would the proposed solution work with this code?

  void foo() {
    char *p = malloc(...);
    defer free(p);
    ...
    {
      FILE *p = fopen(...);
      defer fclose(p);
      ...
    }
    return;
  }
Would it run both deferred statements, each with the correct argument?
gustedt 104 days ago [-]
Yes, the visibility rules for variables remain exactly the same. The dependent statement of a defer lives in the same scope as the defer is placed.
104 days ago [-]
sim7c00 104 days ago [-]
the amount of effort going into people either:

- straightup forgetting to free shit - writing horrible to read code making it impossible to do cleanup or track allocations

why try to make c into c++ or rust? those languages already exist.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 17:44:51 GMT+0000 (Coordinated Universal Time) with Vercel.