Live data from Hacker News

C: Simple Defer, Ready to Use

gustedt.wordpress.com

91–100 of 157 posts

Re: C: Simple Defer, Ready to Use

#91

I'm a strong believer that if C had defer, many bugs would disappear. The number 1 issue I find myself having when switching from C++ to C is missing RAII (Main C++ way of implementing defer).

> number 1 issue I find myself having when switching from C++ to C is missing RAII

That's because you've become complacent; you've gotten used to the false comfort of destructors. C++ destructors promise that you can ignore object destruction in business logic, and that's a false promise.

Assume you have a function that already has a correct traditional (cascading gotos, or arrow pattern) exit path / error path. Assume that you have the (awkward) defer-based implementation of the same function. Assume you need to insert a new construction step somewhere in the middle. In the defer-based implementation, you insert both the new action and its matching "defer" in the same spot.In the traditional implementation, you locate the existent construction steps between which you insert the new construction step; you locate the corresponding existent destruction steps (which are in reverse order), and insert the new destruction step between them. The thinking process is more or less the same, but the result differs: without defer, your resultant source code shows what will actually happen on the exit path, and in precisely what order, and you can read it.

I think defer is awful.

Re: C: Simple Defer, Ready to Use

#92
One big flaw in Go-style `defer` is that error handling and scoping are an afterthought, and it's disappointing to see this is not improved upon in this proposal (at least as I understand the proposal).

I do think this matters in practice. If I have a `func whatever() error` IME it's a common to accidentally do something like `defer whatever()` without catching handling the error. To work around that you'd need to do something like the following.

    var err error
    defer func() {
        err = whatever()
    }
For me personally: ugh. I understand the "received wisdom" is to just structure your code differently. I don't think that's reasonable, because cleanup code is often complicated, often can fail, and is generally vastly less well-tested/exercised. YMMV, my experience is that, because of these things, the dispose-path bugs tend to be disproportionately bad. Error handling facilities that treat the dispose path as by-default not worth insuring are IMO setting users up to have a very bad time later.

While we're fixing up the error handling, I really think it's not an accident that "`defer` in a loop does not terminate in the loop, but at the end of the function" is such a frequent misunderstanding. Yes the docs are clear, but all languages spend a lot of time training you on lexical scoping, and lexically-defined disposal lifetimes (e.g., C#'s `using` or Python's `with`) are a natural extension of that training. Those implementations to varying degrees have the same error handling problem, but with some effort I really think a better world is possible, and I don't think we have to expand this to full-on RAII to provide such semantics.

Re: C: Simple Defer, Ready to Use

#93

I'm a strong believer that if C had defer, many bugs would disappear. The number 1 issue I find myself having when switching from C++ to C is missing RAII (Main C++ way of implementing defer).

...and a we would get a host of new bugs that would be a lot harder to fix. Invisible jumps are very bad.

> Invisible jumps are very bad

Agreed; they're terrible. Implicit sucks, explicit rules.

Re: C: Simple Defer, Ready to Use

#94

Earlier quoted context omitted.

...and a we would get a host of new bugs that would be a lot harder to fix. Invisible jumps are very bad.

Nearly every modern language supports defer in some sense. Unless you are talking about RAII hiding the defer, but that's not the case with a custom Defer type or similar that takes a closure in the constructor. Defer is a way better solution than having to cleanup in every single failure case.

> cleanup in every single failure case

you're only tempted to clean up [fully] in every single failure case if you don't know how to implement cascading gotos or the arrow pattern.

Re: C: Simple Defer, Ready to Use

#96
post #27

Does C really need this? Do languages need to grow in this way? The overriding virtue of C is simplicity.

> Does C really need this?

No.

> Do languages need to grow in this way?

No.

> The overriding virtue of C is simplicity.

It's no longer simple, especially not with the C11 memory model (which got retrofitted from C++11, and is totally incomprehensible without reading multiple hundreds of pages of PhD dissertations); however, gratuitously complicating C shouldn't be done.

Re: C: Simple Defer, Ready to Use

#97

I don't think "you can implement this slightly modified version of the proposal in GCC macros" is a good reason to change the defer proposal. It doesn't work in clang¹ or any other non-GCC C implementation, a trailing semicolon after the close brace is an ugly wart, and saying "if you want to implement this all you have to do is implement [[gnu::cleanup]] and nested local functions" is presumably much more work for o…

Why do you think you need executable stacks for nested functions?

I don't know what GCC is doing, but functional programming languages usually 'compile away' the nesting of functions fairly early, see https://en.wikipedia.org/wiki/Lambda_lifting

Update: Oh, I see, it's because GCC doesn't want to change how function are passed around ('function pointers' in C speak), so they need to get creative.

Functional languages use something closer to what C people would call a 'fat pointer' to fix this.

Re: C: Simple Defer, Ready to Use

#98
post #74

Earlier quoted context omitted.

Nice, if this is reliable across gcc versions and optimization levels, I might consider it for future stuff. Though making it such that treesitter and other tools dont barf on it would still need investigation.

I've used nested functions for a very long time with gcc and they are completely reliable. Since my code is embedded without an MMU the oh noes executable stack doesn't fill me with any dread. It's unfortunate a lot of the standards guys are horrified by anything that isn't C89. Because if the executable stack is an issue it's worth fixing. Side note: 20 years ago people thought if they made the stack non executable…

> Side note: 20 years ago people thought if they made the stack non executable that would totally fix stack smashing attacks and unfortunately it only slows down script kiddies.

Slowing them down is good. And: separating data and code helps simplify managing the caches.

Re: C: Simple Defer, Ready to Use

#100

Earlier quoted context omitted.

> I really, really do not like what that could do to the readability of code if it was used liberally. > It's like GOTOs, but worse, because it's not as visible. > C++'s destructors feel like a better/more explicit way to handle these sorts of problems. But what C++ gives you is the same thing: > It's like GOTOs, but worse, because it's not as visible. ! The whole point of syntactic sugar is for the machinery to be h…

> The whole point of syntactic sugar is for the machinery to be hidden And when the machinery fails, you'll not only have the machinery to debug, but the syntactic sugar too.

Still, the machinery gets more eyeballs and scrutiny to how it works than what some Joe's given codebase does, so chances are that the cause of failure will be somewhere on Joe's side. Then, the code will still need to be debugged, and you'll be glad having to deal with well-known implementations of DRY rule under "syntactic sugar" category instead of whatever Joe happened to come up with instead. Or, maybe there was no DRY rule in Joe's mind and therefore there will be a lot more code to maintain and debug.
Post reply on HN