Live data from Hacker News

A Defer Mechanism for C

gustedt.wordpress.com

211–220 of 248 posts

Re: A Defer Mechanism for C

#211
post #60

Zig has defer and errdefer. C should just steal however Zig does it.

I love Zig, and I'm looking forward to it continuing to mature. I've been waiting for a specific compiler TODO to be resolved (something like initializing union bitfields) that (at least as of 0.7.0) wasn't yet implemented, because I was using it in a toy OS for some GDT stuff.

Had some difficulty with toy os gdt too! I think what you are looking for is the packed union bug.

I would suggest doing it "the hard way", one thought would be treating it as an array (comptime known length) of u8

Re: A Defer Mechanism for C

#212

Earlier quoted context omitted.

What? Any statement inside a while loop could mutate the loop variable and then all bets are off. You have to check every line of a while loop to know what’s going on. Heck, another thread could hold a pointer aliasing the loop variable and then mutate it, causing the loop to terminate for no obvious reason.

My point is that you know at the end of the while loop, it's going to test the loop variable you wrote at the beginning, no matter what's in the loop body. The fact that other things could change the variable isn't relevant. With defer, you don't know what will happen at the end of a block without looking through the whole thing.

Why does it matter so much what code runs at the end of a block?

Re: A Defer Mechanism for C

#213

Earlier quoted context omitted.

Every defer statement is ultimately pushing a lambda into some stack that is executed when the defer block is exited. With an exception of setjmp/longjmp, I can't think of an existing C construct with similar run-time complexity.

That's indeed tricky: if there's a for loop inside the guarded block which generates many defers, a stack would have to be created to handle them somehow.

The defer would just run at the end of each iteration.

Re: A Defer Mechanism for C

#214
post #105

GCC has a similar extension for defering to end of scope, which is basically most of use cases(cleanup function is usually free or some destructor/sanity check function). https://echorand.me/site/notes/articles/c_cleanup/cleanup_at...

This is amazing, thanks! It's a bit different though - it executes a cleanup function when the variable goes out of scope, rather than a statement at the end of the enclosing guard block. It might be better though - it avoids some footguns that would be possible with a defer implementation.

Clang has this too. Libraries like Glib and software like Systemd use this mechanism. Standardizing it would be nice.

Re: A Defer Mechanism for C

#215
post #23
post #6

Earlier quoted context omitted.

> One of the key interest of C compared to more recent languages is that everything is explicit. There's nothing implicit about defer.

Deferred actions are not explicit at the exit points. That's quite implicit. You can no longer reason about a local piece of code; you now have to know its lexical nesting up to top level to see if it's inside a guard block that might trigger hidden behavior.

> Deferred actions are not explicit at the exit points.

They are no less explicit than the actions performed at the end of iterations of for or while loops. In general, for C, understanding the behavior of code requires understanding “is it in a block, and if so what kind of block”; guard blocks would be not generally different.

Re: A Defer Mechanism for C

#216

Earlier quoted context omitted.

Goto isn't nonlocal. You know you'll only every jump from them, and that you'll only ever jump to the specified label.

The specified label is what makes goto no-local. You need to check the whole codebase to find out where you'll land. By your definition only [1] "come from" would be non-local. [1] https://en.wikipedia.org/wiki/COMEFROM

FWIW, Normally the term "non local goto" is reserved for control transfer beyond the current activation frame. So C goto is strictly local, bit longjmp would be non-local.

Re: A Defer Mechanism for C

#217
post #88

Earlier quoted context omitted.

I don't believe that VLAs are actually deprecated as such, but have been moved to an "optional" feature for implementation in C11. There's a feature macro to check, __STDC_NO_VLA__, but I don't think the feature is actually disappearing from the standard any time soon.

I wonder what is a good use for VLAs btw. I used it for a string padding function to create the padding with a simple loop before a call to strjoin. Is that a bad idea or a legitimate use case?

Don't use VLAs for anything that actually requires an allocation. However, VLAs are quite useful when you have a buffer and want to access it like an array.

Re: A Defer Mechanism for C

#218

Earlier quoted context omitted.

I agree. I have tried to come up with some reasonable way to constrain macros, but its really hard!

The way to do it is, survey your macro use to identify common patterns, specify them formally, and then write a computer program that allows those patterns specifically but no others. In other words, design a higher-level language. ;)

Something like https://wiki.gnome.org/Projects/Vala ?

Re: A Defer Mechanism for C

#219

Earlier quoted context omitted.

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);…

Closures capturing iteration variables by reference is a common footgun in many languages unfortunately.

Re: A Defer Mechanism for C

#220

Earlier quoted context omitted.

I think this example demonstrates the opposite of what you intended. Defer doesn't give you anything that you can't already do. In your last four code blocks, you demonstrated three alternate ways to implement the functionality you want without defer. Defer is just a slightly different, arguably nicer fourth way to implement the same code. Language features should be orthogonal. A new language feature should add some…

> In your last four code blocks, you demonstrated three alternate ways to implement the functionality you want without defer. My, I didn't think it was possible to miss the point like that. Are you even arguing in good faith? Let's examine for a moment the 3 other alternatives. First, we get the "repeat ourselves" problem: when I have several exit points, I must clean up at each exit . And if I edit the code in any w…

You have accused me of arguing in bad faith twice in one post. This is insulting, not to mention against the HN rules ("assume good faith").

Clearly we disagree on whether a syntax change is minor. But first let me repeat the point I made that you ignored in between your accusations: it really is just syntax. Of the four examples in the second part of your post, if we assume defer is implemented like attribute cleanup and fix up the compile errors, your first and fourth example compile to the identical assembly code:

https://godbolt.org/z/n14z5q

https://godbolt.org/z/Ksq1rj

I would argue that the best solution is one you didn't present: move the "business logic" into a separate function, one that takes the necessary resources as arguments. This way you're no longer mixing up resource acquisition error handling with business logic, and the function that acquires the resources can use the nested if statement style (or any other style) with no downsides. No surprises here, it again compiles to the identical assembly code:

https://godbolt.org/z/b375E9

In my opinion the nested if style is better than using defer because it's completely linear with no backward jumps. But even if you disagree you can hardly complain about cleanup code being far from init code because the whole resource handling function is less than 20 lines of code regardless of what cleanup style you chose. It doesn't matter, which is why I argue that it's a minor syntax change not worthy of addition to C.

Post reply on HN