Live data from Hacker News

C: Simple Defer, Ready to Use

gustedt.wordpress.com

151–157 of 157 posts

Re: C: Simple Defer, Ready to Use

#151
post #52

Earlier quoted context omitted.

"If -fexceptions is enabled, then cleanup_function is run during the stack unwinding that happens during the processing of the exception. Note that the cleanup attribute does not allow the exception to be caught, only to perform an action. It is undefined what happens if cleanup_function does not return normally." [1] While it's true that it -fexceptions is disabled for C by default, some C libraries need to enable i…

Ah, I forgot about -fexceptions, Thanks. Though I need to point out it's non-default and rarely used, and in particular: > some C libraries need to enable it anyway if they want to interact with C++ exceptions this way. For example C++ requires that qsort and bsearch propagate the exception thrown by the comparison callback normally -fexceptions is not needed for this, C++ exceptions will just transparently bubble th…

-fexceptions used to be the only control for emitting unwind tables, IIRC, which C codebases may have desired for backtraces. (Now there is -fasynchronous-unwind-tables.)

Re: C: Simple Defer, Ready to Use

#152
post #73

I once worked on a student robotics project where if you didn't gracefully shut down the connection to a specialized camera, it had a significant chance of physically bricking the camera. We were using C++ and essentially instrumented code review processes, despite being a student group, to ensure nothing was ever called in a way where the destructor wouldn't be called - and broke out vision processing into a separat…

Yikes. What is it with high school robotics and buggy camera APIs? Back when I competed, we were using Java with the FRC provided vision library. When we tried adding the camera, we discovered that our Java code started to segfault after a few minutes of running. This turned out to be comming from the native camera code being invoked through the FFI. This wasn't as bad as what you saw, as a restart would fix it (for…

Starting a video stream in the ios dji sdk would permanently leak some 150MB of memory and I never found the cycle. So I just made sure the leak could only occur once. There really is something about video..

Re: C: Simple Defer, Ready to Use

#153
post #4

Do defers get processed if an exception is thrown? I know C doesn’t have exceptions, but real world C code often interacts with C++ code. So it is necessary imo to define that interaction. In C++ we have something pretty similar already in the form of Folly ScopeGuard (SCOPE_EXIT {}).

`extern "C"` functions coded in C++ should really not throw! I would say that they MUST NOT throw.

In practice, they can though. So whether they should or not doesn't have much bearing on the situation.

Re: C: Simple Defer, Ready to Use

#154
post #119

Earlier quoted context omitted.

> The modern goto is not the one he wrote about. It is tamed and fits into the structured programming paradigm. No, it doesn't, not the goto of C or C++ (which tames it a smidge because it has to ). That's the disconnect you have. It's not fine just because you can't go too crazy and smash other functions with it anymore. You can still go crazy and jump into the middle of scopes with uninitialized variables. You can…

"You can still go crazy and jump into the middle of scopes with uninitialized variables." It is difficult when speaking across languages, but in many cases, no, you can't. https://go.dev/play/p/v8vljT91Rkr C isn't a modern language by this standard, and to the extent that C++ maintains compatibility with it (smoothing over a lot of details of what that means), neither is it. Modern languages with goto do not generall…

It doesn't even need to be a modern language to protect against that:

  BEGIN
    INT x := 1;
    print(("x is", x, newline));

    GOTO later;

    INT y := 2;

   later:

    print(("y is", y, newline))
  END
for which we have:

  $ a68g goto.a68 
  x is         +1
  11           print(("y is", y, newline))
                            1           
  a68g: runtime error: 1: attempt to use an uninitialised REF INT value (detected in [] "SIMPLOUT" collateral-clause starting at "(" in this line).
Although admittedly it is a runtime error.

However if y is changed to 'INT y = x + 2;', essentially a "constant", then there is no runtime error:

  $ a68g goto.a68 
  x is         +1
  y is         +0

Re: C: Simple Defer, Ready to Use

#155

Everyone that doesn't like a new standardized defer is doing https://knowyourmeme.com/memes/no-take-only-throw but as "no forest, only trees" The explicit alternative for defer is an unreadable mess. And guess what? If you really need to see it, we should having tooling that desugars the defer to gotos. That's the best of both worlds! - The "master" source code is high level "say what I mean" - Low level view that is…

> You really should be able to express the required cleanup semantics with "defer"

I'm perfectly able to do that, I just don't want it, because the result is terrible. The syntax no longer shows what happens when and where.

And, if you look at n3434 https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3434.htm>, you can see the following gem:

> the deferred block may itself contain other defer blocks, that are then executed in chain according to the same rules

This is the worst possible outcome. It means that a continuation passing style sub-language gets embedded in C.

    {
      defer { a(); defer { b(); }; c(); };
      defer { d(); };
    }
At the final closing brace shown, d() will be invoked first, then a(), then c(), then finally b(). Syntactically, the invocations appear in a-b-c-d order in the source code, but the actual execution is neither that nor the inverse d-c-b-a nor the single-level inverse order d-a-b-c.

THAT is an unreadable mess. Good luck debugging that. Not dissimilar to chaining futures in modern async C++ (lambdas deeply nested in lambdas). Which is an abomination.

Your source code syntax is now completely detached from the execution order within a single thread.

Good luck getting that past code review.

Your reference to "no take only throw" makes no sense to me. The gist of that meme is "various characters making contradictory demands". Nobody is making demands here. There's no need for an "explicit alternative". Just don't defer at all, regardless of style. Write the error path / exit path explicitly, using gotos or the arrow pattern.

Re: C: Simple Defer, Ready to Use

#156

Everyone that doesn't like a new standardized defer is doing https://knowyourmeme.com/memes/no-take-only-throw but as "no forest, only trees" The explicit alternative for defer is an unreadable mess. And guess what? If you really need to see it, we should having tooling that desugars the defer to gotos. That's the best of both worlds! - The "master" source code is high level "say what I mean" - Low level view that is…

> You really should be able to express the required cleanup semantics with "defer" I'm perfectly able to do that, I just don't want it, because the result is terrible. The syntax no longer shows what happens when and where. And, if you look at n3434 https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3434.htm >, you can see the following gem: > the deferred block may itself contain other defer blocks, that are then exe…

d, a, c, b makes perfect sense to me --- it's naturally compositional!

There is a style of grey-beard that never wants to reason in parts. He simple loads the whole program into his big brain and works from there: trees through the eyes, forest (maybe, certainly subjective) in the mind.

I too am big-brained, but I wholly reject this approach --- it makes for code that is sorely lacking in motivation --- all "what", no "why".

With "goto" I need to reconstruct what the arbitrary control flow means. It might be cleanup, it might be something else entirely! With "defer", we have the essence of RIAA without any bad OOP nonsense. The cleanup obligations themselves are literal in the code.

You don't need to know the order d, a, c, b run in 99% of the time. The reverse-order semantics indicate the LIFO semantics, which should be enough and tied to e.g. the deadlock avoidance or inter-resources-that-need-cleanup dependency management. And again, whenever you do want to see what the actual order is, the desugarer should always be a click away, perfectly your questions.

Re: C: Simple Defer, Ready to Use

#157

Earlier quoted context omitted.

> You really should be able to express the required cleanup semantics with "defer" I'm perfectly able to do that, I just don't want it, because the result is terrible. The syntax no longer shows what happens when and where. And, if you look at n3434 https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3434.htm >, you can see the following gem: > the deferred block may itself contain other defer blocks, that are then exe…

d, a, c, b makes perfect sense to me --- it's naturally compositional! There is a style of grey-beard that never wants to reason in parts. He simple loads the whole program into his big brain and works from there: trees through the eyes, forest (maybe, certainly subjective) in the mind. I too am big-brained, but I wholly reject this approach --- it makes for code that is sorely lacking in motivation --- all "what", n…

> There is a style of grey-beard that never wants to reason in parts. He simple loads the whole program into his big brain and works from there

Well said.

I'm reluctant to trust composability in these languages because of abstractions that were supposed to be composable, but turned out as non-composable. Separation of concerns, single responsibility etc are all good ideas, but code that is less explicit seems to make those things harder to verify. (I feel completely differently about composability in genuinely functional, managed programming languages.) In a way, C's qualms should have been mitigated by C++ already; but in fact, C++ has only replaced a small set of problems with a huge one.

> With "goto" I need to reconstruct what the arbitrary control flow means

I agree with your point in general (reading code means reconstructing the whole from the parts, which is more difficult than writing code, i.e., deconstructing the whole into parts) -- but not in this specific case. A proper cascade of gotos is very recognizable. And a broken cascade is fairly recognizable too.

> You don't need to know the order

My experience with C++ destructors (which I didn't write) suggests otherwise. "Trust, but verify"; and for that, explicit code is better. IMO.

> the desugarer should always be a click away

Source code should be readable without external tools, IMO.

Post reply on HN