News for hackers. Stuff that matters.
Re:A Defer Mechanism for C by greatgib on Monday December 14, 2020 @08:44AM (#25417383)
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, just because a destructor was randomly called at the wrong time.

Re:A Defer Mechanism for C by rseacord on Monday December 14, 2020 @12:23PM (#25420221)
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 by simias on Monday December 14, 2020 @03:46PM (#25423044)
I would argue that the C for loop is a rather awkward construct. It's found its way in many languages, so I think most people are used to its idiosyncrasy but it's not great if you try to take a fresh look at it.

I think the best defense of this syntax is that it makes writing basic iteration a bit nicer without having to add boilerplate (the iconic `for (i = 0; i < n; i++)`) but then I would argue that the real problem is that C is severely lacking in the iteration department and this is a rather obvious hack (that languages like Javascript felt the need to copy wholesale, for some insane reason).

Re:A Defer Mechanism for C by scythe on Monday December 14, 2020 @10:15PM (#25426548)
The nonlocality can be eliminated by replacing the `guard {}` block with a `resolve;` statement to be placed at the end of a block containing multiple `defer`s. This also reduces nesting and solves the question of where the `defer` is executed and makes it easy to grep to the location where the `defer` statement will be executed. Of course, it would be a syntax error to use `defer` without a following `resolve;`.
Re:A Defer Mechanism for C by pornel on Monday December 14, 2020 @01:44PM (#25421393)
"Explicit" in language design is catch-all term for several different things: https://boats.gitlab.io/blog/post/2017-12-27-things-explicit... (I highly recommend this article.) Defer is still manual and local, just less noisy and burdensome.

Just like every C feature: if you know how it's implemented (and optimized), you will know what is going to happen. All big C compilers already support stack frames and unwinding, so it's not even entirely novel functionality.

While you may object it's not entirely obvious how unwinding is going to be implemented, OTOH in functions with complex control flow it can be easier to understand which `defer`s are going to be run, as opposed to following nested `else` statements, or reasoning about the program state from all `goto cleanup` locations.

Re:A Defer Mechanism for C by whatshisface on Monday December 14, 2020 @10:11AM (#25418411)
>One of the key interest of C compared to more recent languages is that everything is explicit.

With macros this is not exactly true. I gesture towards the GObject system for an extremely complex situation in big important production software.

Re:A Defer Mechanism for C by quelsolaar on Monday December 14, 2020 @12:02PM (#25419875)
I agree. I have tried to come up with some reasonable way to constrain macros, but its really hard!
Re:A Defer Mechanism for C by whatshisface on Monday December 14, 2020 @03:09PM (#25422561)
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. ;)
Re:A Defer Mechanism for C by desiderantes on Monday December 14, 2020 @08:09PM (#25425774)
Re:A Defer Mechanism for C by coldtea on Monday December 14, 2020 @08:50AM (#25417448)
>One of the key interest of C compared to more recent languages is that everything is explicit.

There's nothing implicit about defer.

Re:A Defer Mechanism for C by tangus on Monday December 14, 2020 @09:09AM (#25417671)
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.

Re:A Defer Mechanism for C by coldtea on Monday December 14, 2020 @09:31AM (#25417940)
That's still local at the scope level though, which is quite acceptable.

Plus, to handle the free or the leak if you had forgotten to free a resource at the exit point would also require to "know its lexical nesting up to top level".

Between goto and longjump and co, C has much worse non-local behavior than defer.

Re:A Defer Mechanism for C by josephcsible on Monday December 14, 2020 @11:54AM (#25419762)
Goto isn't nonlocal. You know you'll only every jump from them, and that you'll only ever jump to the specified label.
Re:A Defer Mechanism for C by coldtea on Monday December 14, 2020 @01:50PM (#25421496)
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

Re:A Defer Mechanism for C by gpderetta on Monday December 14, 2020 @07:55PM (#25425660)
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 by josephcsible on Monday December 14, 2020 @02:46PM (#25422243)
> the whole codebase

You can't goto out of a function, and you know there's exactly one such label inside it. If goto isn't local, then neither are function calls, since the function could be defined anywhere in the codebase.

Re:A Defer Mechanism for C by coldtea on Tuesday December 15, 2020 @04:25AM (#25428518)
>You can't goto out of a function

You can with a goto expression and a label address available - though the behavior is undefined in C, so bets are off.

And you can with longjump/setjump more explicitly.

Re:A Defer Mechanism for C by josephcsible on Tuesday December 15, 2020 @08:48AM (#25430229)
Are you talking about this? https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html It's a GNU extension, not part of standard C at all.
Re:A Defer Mechanism for C by dnautics on Monday December 14, 2020 @10:52AM (#25418913)
it's control flow. What you are saying is like saying "while loops are implicit: you can no longer reason about code; you now must think about the state of the check boolean to see if it will trigger the hidden behaviour of going back to the top of the loop because there's no explicit "go back to the top of the block" statement.
Re:A Defer Mechanism for C by josephcsible on Monday December 14, 2020 @11:56AM (#25419794)
With a while loop, to know what happens at the bottom of the block, you only need to check the top of the block. With defer, to know what happens at the bottom of the block, you need to check the entire contents of the block.
Re:A Defer Mechanism for C by chongli on Monday December 14, 2020 @05:11PM (#25424077)
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.

Re:A Defer Mechanism for C by josephcsible on Monday December 14, 2020 @05:28PM (#25424243)
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.
Re:A Defer Mechanism for C by saagarjha on Monday December 14, 2020 @07:33PM (#25425457)
Why does it matter so much what code runs at the end of a block?
Re:A Defer Mechanism for C by IAmADHDToo on Monday December 14, 2020 @03:02PM (#25422460)
> With a while loop, to know what happens at the bottom of the block, you only need to check the top of the block.

Anyway, what could possibly happen at the end of a while loop, besides going back to the begining for the test?

Besides, if you have a bug in your code, you will have to look at the whole block anyway.

Re:A Defer Mechanism for C by SPBS on Monday December 14, 2020 @11:02AM (#25419043)
It's as much hidden behaviour as destructors being called magically on an object that goes out of scope. In practice it doesn't hinder understandability as much as you think.
Re:A Defer Mechanism for C by josephcsible on Monday December 14, 2020 @11:53AM (#25419738)
> destructors being called magically on an object that goes out of scope

Which C doesn't have.

Re:A Defer Mechanism for C by dragonwriter on Monday December 14, 2020 @07:42PM (#25425548)
> 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 by sramsay on Monday December 14, 2020 @10:01AM (#25418299)
I wondered about this. Say I have a block, at the end of which I free a bunch of memory. I also have a bunch of other exit points within the block (mostly for catastrophic errors, say). Would a deferred free only apply to the outer scope? Because if so, I really don't see the point at all.
Re:A Defer Mechanism for C by dnautics on Monday December 14, 2020 @10:46AM (#25418831)
yes, you always run defer. I don't see it in this proposal, but Most languages/frameworks that implement a defer also implement a "error defer" which only gets triggered on some sort of labeled early exit scenario, and some implement "success defer": (i believe this is scopeguard: https://www.youtube.com/watch?v=WjTrfoiB0MQ)
Re:A Defer Mechanism for C by apankrat on Monday December 14, 2020 @10:03AM (#25418325)
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.
Re:A Defer Mechanism for C by IAmADHDToo on Monday December 14, 2020 @02:54PM (#25422363)
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.
Re:A Defer Mechanism for C by saagarjha on Monday December 14, 2020 @07:34PM (#25425467)
The defer would just run at the end of each iteration.
Re:A Defer Mechanism for C by skohan on Monday December 14, 2020 @05:43PM (#25424366)
Is it actually a lambda, or is it just essentially appending statements to the end of the scope?
Re:A Defer Mechanism for C by anfilt on Monday December 14, 2020 @08:36PM (#25425984)
that depends for instance you can safely call free on NULL so you could just append.

However, for some things you can only free/return resources if you successfully created that resource. At which point you would need to use something like a stack.

Re:A Defer Mechanism for C by skohan on Monday December 14, 2020 @05:47PM (#25424404)
I think the broader and more accurate point is that defer adds cognitive load to reasoning about the order of execution. It's true that defer (at least everywhere I've seen it executed) is totally explicit and deterministic, but in the case of multiple defers in the same block it can take some thought to reason through exactly what happens when.
Re:A Defer Mechanism for C by Asooka on Monday December 14, 2020 @10:58AM (#25418987)
I view it as another element of structured programming. It can be expressed as a series of "goto" statements, same as for, while, do, switch. It's just one they didn't think of back when C was being designed. If it was in from the beginning, nobody would think it strange.

That said, it's still debatable if it's useful, given that you can achieve the same thing with the

    struct some_resource resource;
    do {
        resource = allocate(...);
        if (!resource) break;
    } while(0);
    if (resource) dispose(resource);
I can see it as a good thing because you have the dispose statement next to the allocate statement, which makes the logic easier to follow, but the implementation may have caveats which make it actually harder to reason about, e.g. see the other thread about capture value vs capture reference - C will most probably need to capture by reference, which means that modifying "resource" later on changes the meaning of the deferred statement.
Re:A Defer Mechanism for C by btilly on Monday December 14, 2020 @04:48PM (#25423826)
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.

This has not been true in many years. C appears to be such a language, but optimizing compilers have learned how to find and optimize undefined behavior in code that most C programmers don't realize is unsafe. As a result there can be a considerable gap between the code as clearly intended, and the code that will be generated.

See https://blog.regehr.org/archives/213 for more on this. Including real world examples of things like validation checks being elided by the compiler, leading to real-world vulnerabilities in programs which clearly have checks to avoid exactly those vulnerabilities.

Re:A Defer Mechanism for C by Someone on Monday December 14, 2020 @10:54AM (#25418943)
So, what’s the alternative? Not freeing resources is a very common error; it would be nice if the compiler could help detecting it.

A possible explicit solution I can think of is to introduce a new function attribute that gives the name of their ‘cleanup’ function (so that the compiler would know fopen needs a fclose, for example) and a compiler that uses these attributes to issue a warning if a function has a path that calls a function and doesn’t either call its cleanup function or returns its result.

I don’t know whether that would cover all bases, though.

Re:A Defer Mechanism for C by zabzonk on Monday December 14, 2020 @08:51AM (#25417463)
Destructors are not called randomly.
Re:A Defer Mechanism for C by greatgib on Monday December 14, 2020 @09:17AM (#25417760)
As replied by someone else, by 'random' I did not mean really 'randomly', because the machine is determinist and follow a logic.

But more that there is so much magic and abstractions that it is very hard for a dev to have a clear view of what is going on and what to expect. He has to 'guess' instead of just read the code. For that, I guess that it is similar to the current question of accountability of decisions made with deep learning algorithms.

As an example of my point, I would refer to the 'garbage collection' issues of a language like 'java'. GC will happen at a logically defined point like 'dirty mem > 100m' but from the developer point of view, his logic could suddenly lag unexpectedly in a middle of a simple operation because the GC was triggered by internal magic. It is very hard for a dev to be able to determine the memory usage at different points of the code and so have a certitude of when this operation could happen.

Re:A Defer Mechanism for C by zabzonk on Monday December 14, 2020 @09:57AM (#25418261)
> As an example of my point, I would refer to the 'garbage collection' issues of a language like 'java'

Two languages could hardly be less alike than Java and C++ - the latter is not garbage collected and where destructors are called is completely predictable.

Re:A Defer Mechanism for C by cle on Monday December 14, 2020 @10:19AM (#25418500)
You’re missing the point. The two are alike in that something significant is happening that you as the programmer did not explicitly tell it to do. The specific mechanism really isn’t an important distinction here.
Re:A Defer Mechanism for C by DougBTX on Monday December 14, 2020 @10:45AM (#25418819)
There is no guarantee when a Java finalizer will run, whereas a C++ destructor is guaranteed to run based on the code that you write. That's a big difference which has practical implications, using something with non-guaranteed behaviour as an example of why something with guaranteed behaviour is hard to predict seems like a misunderstanding.
Re:A Defer Mechanism for C by steveklabnik on Monday December 14, 2020 @01:06PM (#25420896)
I believe the situation is the same in C++, that is, if you have a shared_ptr cycle, the destructor will never be called, no?

It is more deterministic in C++, but you can still call exit(). (which is even more straightforward than the refcount cycle)

Re:A Defer Mechanism for C by jcelerier on Monday December 14, 2020 @02:01PM (#25421646)
> I believe the situation is the same in C++,

in the specific case of shared_ptr, which are a small part of codebases, if they are even used - for instance an immense amount of C++ GUI programs use Qt which doesn't use shared_ptr-like ownership semantics but instead a tree-of-objects model which does not have this issue. In contrast in Java / C# any object that has a reference to another is at risk.

Re:A Defer Mechanism for C by steveklabnik on Monday December 14, 2020 @06:32PM (#25424872)
Sure. That’s why I said “more deterministic.” But even a rare event disproves the guarantee.
Re:A Defer Mechanism for C by DougBTX on Tuesday December 15, 2020 @01:35PM (#25433349)
Sorry, I meant that the behaviour is guaranteed, not that the destructor is guaranteed to be called. So in the Rc cycle example the guarantee is that the destructor won’t be called.
Re:A Defer Mechanism for C by steveklabnik on Tuesday December 15, 2020 @06:28PM (#25436845)
Ah, then I misunderstood you, yes. My bad!
Re:A Defer Mechanism for C by cle on Monday December 14, 2020 @04:15PM (#25423414)
Of course it has practical implications and is harder to predict, but neither of those are what this conversation is about.
Re:A Defer Mechanism for C by zabzonk on Monday December 14, 2020 @10:36AM (#25418715)
If you write a destructor then you know exactly under what circumstances in C++ code it will be used (this is one of the major features of the language). This specific mechanism is extremely important.
Re:A Defer Mechanism for C by cle on Monday December 14, 2020 @10:46AM (#25418828)
Nobody is claiming that it isn’t well-defined nor unimportant.
Re:A Defer Mechanism for C by jcelerier on Monday December 14, 2020 @11:56AM (#25419787)
> something significant is happening that you as the programmer did not explicitly tell it to do.

when I put some object on automatic storage in C++ I do so because I explicitely want it to go away when its scope is left (either by reaching } or through an exception)

Re:A Defer Mechanism for C by sillysaurusx on Monday December 14, 2020 @09:02AM (#25417598)
"Smart pointers go brr"

It's not really random, but it's quite hard to follow.

Re:A Defer Mechanism for C by eropple on Monday December 14, 2020 @09:54AM (#25418197)
It shouldn't be. std::unique_ptr is very clear.

(std::shared_ptr should be used only when absolutely required and should be kept under close watch the whole time.)

Re:A Defer Mechanism for C by djmips on Monday December 14, 2020 @10:46AM (#25418820)
searches codebase I'm working on... finds thousands and thousands of std::shared_ptr (many involve a typedef, so there are even more...) eep!

Should I be alarmed?

Re:A Defer Mechanism for C by cesarb on Monday December 14, 2020 @10:58AM (#25419000)
If that codebase is older than C++0x, many of these std::shared_ptr could be taking the place of std::unique_ptr (which didn't exist back then; std::auto_ptr was a footgun). If you're in the mood for a refactoring, you could take a look at each one and see whether then can be replaced by std::unique_ptr, or whether they really have shared ownership semantics (which is what std::shared_ptr should be used for).
Re:A Defer Mechanism for C by usefulcat on Monday December 14, 2020 @11:29PM (#25427016)
Neither std::unique_ptr nor std::shared_ptr existed prior to c++11. Perhaps you're thinking of boost::shared_ptr or boost::scoped_ptr, both of which have existed in one form or another for somewhere around 20 years.
Re:A Defer Mechanism for C by Arnavion on Monday December 14, 2020 @02:55PM (#25422377)
Not necessarily. The concern about when the shared_ptr'd value runs its destructor only matters if the value's destructor is something you care about.

If you have a situation where a value needs to be shared between multiple other values, but also the value is trivial enough that it doesn't matter when it's destroyed or what it does when it's destroyed, but also the value is not so trivial that you can copy it instead of using a refcounting pointer, then std::shared_ptr is fine.

Re:A Defer Mechanism for C by eropple on Monday December 14, 2020 @02:59PM (#25422415)
"Fine" is probably pushing it, but "not the biggest problem you are likely to have" is probably true. But, like, it's easy to accidentally get yourself into situations where somebody forgot to use std::weak_ptr somewhere and you start leaking memory.

Unless you know you must share objects with multiple potential owners, though, std::unique_ptr is a lot wiser.

Re:A Defer Mechanism for C by Arnavion on Monday December 14, 2020 @03:01PM (#25422446)
Yes, when I wrote:

>but also the value is trivial enough that it doesn't matter when it's destroyed or what it does when it's destroyed"

that precludes the value from having other refcounting pointers that could then create cycles and leak.

Re:A Defer Mechanism for C by hedora on Monday December 14, 2020 @10:58AM (#25418997)
Yes. On reflection, or on ramping up new developers, I think you’ll find that it is unnecessarily difficult to reason about object lifetimes.

There’s a good chance that most of the shared_ptr’s can be replaced by unique_ptr, which has less runtime overhead. More importantly, it documents the intent of the programmer regarding ownership semantics, and the timespan during which the object should be remain valid.

Re:A Defer Mechanism for C by lultimouomo on Monday December 14, 2020 @09:35AM (#25417986)
That's really an argument against smart (to be precise, shared) pointers, not against destructors.
Re:A Defer Mechanism for C by ben509 on Monday December 14, 2020 @09:37AM (#25418011)
If it's too cold for you, it's too cold for your smart pointers. Bring your smart pointers inside during the winter months.
Re:A Defer Mechanism for C by yvdriess on Monday December 14, 2020 @09:48AM (#25418145)
guard/defer is semantically and syntactically as explicit (or implicit) as C's automatic storage class (https://en.cppreference.com/w/c/language/storage_duration), which is the default storage class.
Re:A Defer Mechanism for C by fanf2 on Monday December 14, 2020 @10:18AM (#25418488)
Only if you include dynamically sized arrays, which are deprecated because of their problematic allocation behaviour.
Re:A Defer Mechanism for C by shakna on Monday December 14, 2020 @10:43AM (#25418794)
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.
Re:A Defer Mechanism for C by cassepipe on Monday December 14, 2020 @03:57PM (#25423184)
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?
Re:A Defer Mechanism for C by saagarjha on Monday December 14, 2020 @07:59PM (#25425689)
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 by sudobash1 on Monday December 14, 2020 @04:44PM (#25423774)
> 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.

I would suggest functions like atexit and pthread_cleanup_push as well established counterexamples. Granted this is not a perfect comparison because these are implementable without extending the c language; however, I think they have the same general idea of "defering" cleanup. I think the proposed defer functionality is actually more readable because the defer command will be written much closer to where it will be exited. Compare this to atexit() which may be put anywhere.

Re:A Defer Mechanism for C by Animats on Monday December 14, 2020 @06:35PM (#25424901)
Despite being convenient, I have the feeling that it might be a bad idea.

Probably. It's one of Go's lesser ideas.

C already has a "defer" mechanism in "exit", to close out files and such. Of course, the final I/O status gets lost.

Re:A Defer Mechanism for C by dnautics on Monday December 14, 2020 @10:34AM (#25418691)
> With a finger you can follow the code as it runs and know exactly what is going on and when exactly.

Oh yeah?

https://www.youtube.com/watch?v=Gv2I7qTux7g?t=29m21s

Re:A Defer Mechanism for C by Technically on Monday December 14, 2020 @06:36PM (#25424915)
> 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.

I don't agree! There's a lot of behavior that's implicit and you essentially need to internalize the C standard/compiler behavior to follow. Weak typing, for instance; defaults for memory access/fencing, handling faults, runtime semantics with respect to initialization of the process, cleanup via atexit, and signal handling.

Memorable, sure, but hardly explicit.

Re:A Defer Mechanism for C by brudgers on Monday December 14, 2020 @09:54AM (#25418201)
Despite being convenient,I have the feeling that it might be a bad idea.

What is "I am thinking of using C?"

Re:A Defer Mechanism for C by dkersten on Monday December 14, 2020 @10:27AM (#25418607)
Depending on your platform, it may be your only reasonable choice.
Re:A Defer Mechanism for C by brudgers on Monday December 14, 2020 @11:56AM (#25419791)
I agree completely. For the most part it seems if a person has to think long and hard about it, they probably should not use C and if it is the obvious choice that’s probably because C is ordinary in the context. These days there are probably fewer edge cases than in the past.