Live data from Hacker News

A Defer Mechanism for C

gustedt.wordpress.com

131–140 of 248 posts

Re: A Defer Mechanism for C

#131
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

Based on committee discussion, I think it is unlikely we will attempt to capture the values. The capture will most likely be done by reference. This case of the defer in the loop is frequently cited, probably because it is a problematic case. However, I looked at a lot of real code and the only case I found of resources being allocated in a loop they were allocated at the beginning of the loop and deallocated at the…

Agreed. In Go, defers accumulate and are all called at the end of a function, which I believe is a mistake (but there are practical uses for it). I think it would be much more fitting for C just to have the defer happen at the end of the scope. It would be very easy for compiler devs to implement it too--that above example could be done just by taking the defer block and pasting it at the end of the scope, with zero overhead. More complex control flow can be implemented with a simple goto.

Re: A Defer Mechanism for C

#132
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

Based on committee discussion, I think it is unlikely we will attempt to capture the values. The capture will most likely be done by reference. This case of the defer in the loop is frequently cited, probably because it is a problematic case. However, I looked at a lot of real code and the only case I found of resources being allocated in a loop they were allocated at the beginning of the loop and deallocated at the…

The fact that the loop case is rare does not mean it isn't still problematic. It's creating a new opportunity to shoot yourself in the foot while trying to solve another one. (This is with the separate guard{} block.)

I would strongly agree that the dedicated guard{} block is a bad idea and this should just tie into the innermost scope. I see what this is trying to do ("if (...) { foo.x = malloc(); defer free(foo.x); }") but you don't solve a UI problem (as in, user interface for the programmer to their code) by adding more weird UI.

("Worst" example for shooting yourself in the foot: defer inside macros. Programmer then forgets that the macro contains a defer, and the defer defaults to the function-implicit outer guard block. But it's really in a nested loop. That's gonna be a fun week of debugging... much less of a risk when you have the guarantee in terms of innermost scope.)

Re: A Defer Mechanism for C

#133
post #2

Despite being convenient,I have the feeling that it might be a bad idea. One of the key interest of C compared to more recent languages is that everything is explicit. With a finger you can follow the code as it runs and know exactly what is going on and when exactly. At the opposite, there is c++ that does a lot of things automagically. And it is often hard to understand why you suddenly get a segfault out of blue,…

I've heard this comment more than once. What we are trying to accomplish is to collocate the resource acquisition code with the acquisition release code. This does mean that the release code is removed when where the release occurs (for example, at every location a function returns.

However, this is not without precedence in C. For example, just look at the for loop:

for (clause1; expression2; expression3) statement

expression3 is executed after statement.

Re: A Defer Mechanism for C

#134
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

Some commenters are claiming this issue can be worked around by using by-reference capture, but the following code still requires dynamic allocation: void reverse_bitstream(void * read_ctx, void * write_ctx) { guard { while(has_more_bits(read_ctx)) { int b = read_bit(read_ctx); if (b == 0) defer write_bit(write_ctx, 0); else if(b == 1) defer write_bit(write_ctx, 1); } } }

An example like that is exactly why, in its current form, it's a bad idea. It's too high level. On the surface it seems like it makes code more consise and elegant, but in reality it just pushes the "ugly" implementation details and allocations onto the compiler. When you start writing code that's phrased in terms of high-level compiler features, rather than in terms of what the computer needs to do, then you're no longer writing C.

Re: A Defer Mechanism for C

#135
post #12

Please just standardize the already existing attribute((cleanup)) mechanism which is already being used by lots of Linux software. This new mechanism is incompatible while bringing no benefits.

Please submit a proposal.

Re: A Defer Mechanism for C

#136
post #8

A nice alternative is to wrap malloc and give it a context argument. Then free via the context. This is really the same idea but doesn’t need any extra stuff in the language Or you know... free the things you need to free and move on with your life

That solution doesn't work if you malloc up a structure and stick other things that need cleanup and won't be aware of the context (example: a file descriptor) inside.

Re: A Defer Mechanism for C

#137
post #89
post #66

Earlier quoted context omitted.

ISO has to care about much more platforms than just Linux.

Which is why we want it to be standardized. It's already widely used on Linux and BSDs, the big compilers already implement the underlying functionality (because it's needed by C++), GCC and LLVM implement the exact same extension in an identical way, it does everything that people need, so let's make it work officially.

Still there is a life beyond UNIX clones and gcc/clang.

The C compiler vendor for some embedded CPU with homegrown C compiler for their in-house OS also has a seat at ISO table.

While people here might not care, ISO does.

Re: A Defer Mechanism for C

#138
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

Based on committee discussion, I think it is unlikely we will attempt to capture the values. The capture will most likely be done by reference. This case of the defer in the loop is frequently cited, probably because it is a problematic case. However, I looked at a lot of real code and the only case I found of resources being allocated in a loop they were allocated at the beginning of the loop and deallocated at the…

The simple solution to me seems to be to just ban use of loops hierarchically between a defer statement and a guard block. The guard block should definitely be kept as using scope seems like it confuses and complicates the notion of scope; for instance, it seems like a footgun if "if(some condition that turns out to be always true) {blah blah}" can't be refactored into "blah blah". Also, using scope seems to make it difficult to use defer in macros.

Re: A Defer Mechanism for C

#139
post #107

Earlier quoted context omitted.

I do feel that RAII is a little bit ruined by the fact that you cannot declare anonymous object instances. For example, I cannot write: lock (mutex); Expecting to declare an anonymous lock with the mutex passed in, as this gets parsed as a declaration of a lock called mutex (hopefully lock doesn’t have a default constrictor and I at least get an error). Instead I have to bake my lock: lock l(mutex); Often I have obje…

IIRC, the problem with C++ is that you can declare anonymous object instances... With surprising results (the object is destroyed at the semicolon, instead of being destroyed at the end of the block).

Sure, you can, but its still not the behaviour you'd expect and the problem in my example is that "if it looks like a declaration, it is", so "foo bar;" and "foo (bar);" can be the same thing.

Re: A Defer Mechanism for C

#140

Earlier quoted context omitted.

I do feel that RAII is a little bit ruined by the fact that you cannot declare anonymous object instances. For example, I cannot write: lock (mutex); Expecting to declare an anonymous lock with the mutex passed in, as this gets parsed as a declaration of a lock called mutex (hopefully lock doesn’t have a default constrictor and I at least get an error). Instead I have to bake my lock: lock l(mutex); Often I have obje…

Workaround: #define LOCK(mutex) lock lock##__LINE__(mutex) Or even C compatible scoped locks: #define SCOPED_LOCK(mutex) \ for (int i##__LINE__ = lock_mutex(mutex), 1; \ i##__LINE__ --; \ unlock_mutex(mutex)) Use like SCOPED_LOCK(foo->mutex) { do_stuff(); }

Of course there are workarounds, but that's not the point. First of all, your solution requires macros and the problems that come with that, but also, you now require programmers to remember to use your macro instead of just declaring the instance.

The problem I'm describing is less about the effort of adding a variable name -- that's really not a big deal -- its that its super error prone to remember and these bugs slip through all the time. Requiring a variable name (either explicitly or through a macro like yours) is error prone.

Post reply on HN