pif 3 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...

  • gustedt 3 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 2 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 2 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 2 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 2 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).
sirwhinesalot 2 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 3 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 3 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.

sim7c00 2 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.