Live data from Hacker News

A Defer Mechanism for C

gustedt.wordpress.com

91–100 of 248 posts

Re: A Defer Mechanism for C

#91
post #47

Earlier quoted context omitted.

"Smart pointers go brr" It's not really random, but it's quite hard to follow.

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.)

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

#92
post #85
post #71

Earlier quoted context omitted.

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.

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.

Nobody is claiming that it isn’t well-defined nor unimportant.

Re: A Defer Mechanism for C

#93
post #53
post #23

Earlier quoted context omitted.

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.

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.

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

#94
post #59
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

I got the impression that the proposal still has some aspects of the design that are open for discussion, including whether it should be static or dynamic, and whether it should capture the variables by value or by reference. (These are discussed in pages 13-16)

Yes, see the discussion in the proposal on page 13, under the heading "Should defer statements be static or dynamic?": http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2542.pdf

Re: A Defer Mechanism for C

#95
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.

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

#96
Just use some macros, easy peasy. (gcc computed goto)

    #define DEFER_START(scope_name) void * scope_name = &&scope_name##_end;
    #define DEFER(scope_name, iter_name, func) void * iter_name = scope_name; { \
            scope_name = &&iter_name ##_exe; \
            if (0) { iter_name##_exe: {func} goto *iter_name; } }

    #define DEFER_END(scope_name) scope_name##_start: goto *scope_name; scope_name##_end: {}
    #define DEFER_EXE(scope_name) goto scope_name##_start;

    ...
    {
    DEFER_START(scope);
    void * p = malloc(4);
    if (!p) DEFER_EXE(scope);
    DEFER(scope, free_p, { printf("freep\n"); free(p); });
    void * q = malloc(40);
    if (!q) DEFER_EXE(scope);
    DEFER(scope, free_q, { printf("freeq\n"); free(q); });
    DEFER_END(scope);
    }
you can even macro the free_p/ free_q names with line numbers to shorten the macros, IE DEFER(scope, {});

Re: A Defer Mechanism for C

#97
post #78
post #46

In its current form, this is really stupid, because of something like this: guard { for (int i = 0; i Now the compiler has to: 1. implement some side of capture/closure mechanism to keep all the 'i's to the end of the guard block 2. do dynamic allocation to store the closures so they can be executed at the end did the scope 1 seems like too much work for such a feature, and 2 is a massive no. Implicit dynamic allocat…

I suspect it will come with some really big caveats. For instance what you wrote shouldn't compile, because i's storage duration doesn't extend to the end of the guard block. If you write guard { int i; for (i=0; i I would expect foo(n) to be called n times. That is, variables won't be captured, it would work literally as if you wrote "foo(i)" n times at the end of the guard block. It's a footgun, but everything in C…

The proposal poses object value capture as an open question. See heading "Should object values be captured?" on page 14 of the proposal: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2542.pdf

Re: A Defer Mechanism for C

#98

Re: Should object values be captured? The results from 387 responses (to a Twitter poll) show a 2:1 preference for the value being read at the time the deferred statements are executed (66.9%) rather than when the defer statement encountered(33.1%). Since both options - by value and by reference - may be viewed as reasonable or desirable, neither should be a default. Instead, they both should be using a special synta…

Hopefully not with "^" though! Imagine `x^ ^ ^y`...

Why not have `defer free(p)` capture at deferral execution time, and `defer free(capture(p))` capture at statement execution time?

Re: A Defer Mechanism for C

#99
post #2

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,…

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

#100
post #2

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,…

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.
Post reply on HN