Live data from Hacker News

A Defer Mechanism for C

gustedt.wordpress.com

121–130 of 248 posts

Re: A Defer Mechanism for C

#121
post #41

I’ve always considered “defer” an inelegant kludge in comparison to RAII for automatic cleanup of resources. It’s surprising that the C standards committee is considering adding that to the language. Then again, nearly none of the syntactic C features post-C89 have been very compelling or widely adopted.

Also defer runs even if your code panic which is great.

... which is exactly how C++ RAII behaves when there is an exception ?

Re: A Defer Mechanism for C

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

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

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

#123

Earlier quoted context omitted.

I would strongly disagree on RAII being a kludge in C++. The language feature that enables it is deterministic destructors, whose primary purpose is to ensure that allocated resources are deallocated. RAII is one particular use of destructors. The primary goal is to have the object be the thing that owns a resource, not the calling scope. In terms of usability, destructors allow for resource management that is far ea…

I do feel that RAII is a little bit ruined by the fact that you cannot declare anonymous object instances. For example, I cannot write: lock (mutex); Expecting to declare an anonymous lock with the mutex passed in, as this gets parsed as a declaration of a lock called mutex (hopefully lock doesn’t have a default constrictor and I at least get an error). Instead I have to bake my lock: lock l(mutex); Often I have obje…

Workaround:

    #define LOCK(mutex) lock lock##__LINE__(mutex)
Or even C compatible scoped locks:

    #define SCOPED_LOCK(mutex) \
        for (int i##__LINE__ = lock_mutex(mutex), 1; \
                 i##__LINE__ --; \
                 unlock_mutex(mutex))
Use like

    SCOPED_LOCK(foo->mutex) {
        do_stuff();
    }

Re: A Defer Mechanism for C

#124
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…

Doing defer in a loop is usually a bug in your Go code. Sure there are legit use cases of it, but in ~90% of the time what you really want to do is (in Go, I haven't used C recently so not sure how to do lambdas correctly):

    for i := 0; i 

Re: A Defer Mechanism for C

#125
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…

Based on committee discussion, I think it is unlikely we will attempt to capture the values. The capture will most likely be done by reference.

This case of the defer in the loop is frequently cited, probably because it is a problematic case. However, I looked at a lot of real code and the only case I found of resources being allocated in a loop they were allocated at the beginning of the loop and deallocated at the end. Another option we are considering is to use the scope for the guarded block. In this case, deferred statements would be executed at the end of each iteration of the for loop which would be ideal for this sort of code. For example, you could rewrite this function using defer:

https://github.com/openssl/openssl/blob/a829b735b645516041b5...

like this:

    for (;;) {
        raw = 0;
        ptype = 0;
        i = PEM_read_bio(bp, &name, &header, &data, &len);
        defer {
          OPENSSL_free(name);
          name = NULL;
          OPENSSL_free(header);
          header = NULL;
          OPENSSL_free(data);
          data = NULL;
        }
 ...
        } else {
            /* unknown */
        }
    } // end for loop, run deferred statements

Re: A Defer Mechanism for C

#126
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…

Some commenters are claiming this issue can be worked around by using by-reference capture, but the following code still requires dynamic allocation:

  void reverse_bitstream(void * read_ctx, void * write_ctx) {
    guard {
      while(has_more_bits(read_ctx)) {
        int b = read_bit(read_ctx);
        if     (b == 0) defer write_bit(write_ctx, 0);
        else if(b == 1) defer write_bit(write_ctx, 1);
      }
    }
  }

Re: A Defer Mechanism for C

#127
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…

alloca() is not a standard C primitive. It's only an extension, and its purpose is to dynamically allocate memory that is automatically freed. Using a dynamic amount of stack space is dynamic allocation, it's just not on the heap.

This still makes it really easy to overflow your stack, especially if for example your loop count above can come from user input. This is dangerous for all the same reasons that variable-length arrays are dangerous.

Re: A Defer Mechanism for C

#128
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)

Based on feedback from the committee, I think it's very unlikely a dynamic approach will be used. The static only affects the for loop in terms of whether the deferred statements are run once or for each iteration of the loop, and the only real world use of this I've found so far involves allocating and deallocating resources within each loop iteration which would be supported by the static approach.

Re: A Defer Mechanism for C

#129
post #97
post #78

Earlier quoted context omitted.

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

Yes, you are correct and there wasn't much support for capturing values so we would probably only support capture by reference and wait to see if C solves this problem in general by introducing lambdas.

Re: A Defer Mechanism for C

#130
The panic/recover mechanism would be a disaster. C code is not written with the possibility of stack unwinding in mind and this introduces the possibility of non-visible, non-local control flow at every function call. The simple possibility of that would massively hurt codegen. One of the benefits of writing in C over C++ is that you don’t have to worry about non-local control flow like exceptions.
Post reply on HN