Hacker News new | ask | show | jobs
by WalterBright 2962 days ago
As a longtime C and goto user, defending the practice many times, I discovered something interesting.

My uses of goto can be replaced with nested functions! The code is nicer, cleaner, and the equivalent code is generated (the nested functions get inlined).

Of course, nested functions aren't part of Standard C, but they are part of D-as-BetterC. (D has goto's too, but I don't need them anymore.)

4 comments

Walter, what do you think of nested functions or even normal functions taking an identifier similar to continue / break to be able to jump up the stack precisely: either a certain number of steps, or to a particular calling function.

https://dlang.org/spec/statement.html#continue-statement

also, what do you think of a partial compilation feature / tree shaking https://webpack.js.org/guides/tree-shaking/

Just wanted to mention that GNU C has supported nested functions since forever, it's one of the main reasons I prefer GCC over Clang these days.
Yes and it annoys me no end that clang has refused to implement them as well, as they were part of my codebase as well... How better to implement stuff like qsort() callbacks than with a simple, contextual small function just over it ??

YES it is dangerous due to stacks etc etc but hey, we're grown up adults, not script kiddies.

Clang has Blocks instead of nested functions. There may be patches for gcc-4.2 for Blocks but they didn't make it to the mainline.

https://en.wikipedia.org/wiki/Blocks_(C_language_extension)

> YES it is dangerous due to stacks etc etc but hey, we're grown up adults, not script kiddies.

That is how CVEs are born.

Using a chainsaw without paying attention is how fingers are cut off, using that as an argument against making chainsaws easier to use doesn't make any kind of sense.
Good chainsaws have protection mechanisms builtin.

C does not.

Good for you maybe, projecting that on people who have a clue what they're doing doesn't make sense either. They're mostly messing up chainsaws as well these days, for the same misguided reasons.
That adds a lot of boilerplate though. IMO the best solution is destructors and RAII so that you can return early in case of error and not leave your resource half-initialized. And this way you don't have to repeat your cleanup code in the "deinit" method. Of course if you start adding destructors soon you'll want generics and before you know it you end up with "C with classes" and we all know that's the path towards insanity.
> That adds a lot of boilerplate though.

In practice, it doesn't. (The compiler inlines the code.) I know because I'm pretty picky about this sort of thing - it was why I was using goto in the first place.

> RAII

RAII can work, but it's a bit clunky compared to a nested function call. Additionally, if the code being factored is not necessarily the same on all paths, that doesn't fit well with RAII.

Boilerplate in term of characters typed, not resulting code size. Adding a bunch of function declarations adds a significant amount of noise IMO.

>RAII can work, but it's a bit clunky compared to a nested function call. Additionally, if the code being factored is not necessarily the same on all paths, that doesn't fit well with RAII.

I think I'd need to see an example of what you're talking about then because I don't quite understand how your method works exactly.

Boilerplate doesn't mean (binary) bloat.
Rather than debate, here's an example of replacing goto's with a nested function:

https://github.com/dlang/dmd/pull/6656/files

I guess it's a matter of taste, but I often (not always) prefer less return statements in a function with gotos to error handling/cleanup code. Especially in kernel mode drivers.
Check the asm code generated by your compiler - it may optimize multiple returns into one.
> My uses of goto can be replaced with nested functions

I guess you are not using goto as flow-control then.

That's what I would have said until I tried it :-), and why I said it was interesting!