Live data from Hacker News

Defer available in gcc and clang

gustedt.wordpress.com

221–230 of 262 posts

Re: Defer available in gcc and clang

#221
post #220

Earlier quoted context omitted.

That is a good point. If text rendering is slow, why are you not doing it in parallel? This is what 9rx called out earlier.

Some hypothetical example numbers: if software-rendering text takes 0.1 milliseconds, and I have a handful of text strings to render, I may not care that rendering the strings takes a millisecond or two. But that 0.1 millisecond to render a string is an eternity compared to the time it takes to allocate some memory, which might be on the order of single digit microseconds. Saving a microsecond from a process which ta…

You might not care today, but the next guy tasked to render many millions of strings tomorrow does care. If he has to build yet another API that ultimately does the same thing and is almost exactly the same, something has gone wrong. A good API is accommodating to users of all kinds.

Re: Defer available in gcc and clang

#222

Earlier quoted context omitted.

For concurrent processing you'd probably do something like splitting the file names into several batches and process those batches sequentially in each goroutine, so it's very much possible that you'd have an exact same loop for the concurrent scenario. P.S. If you have enough files you don't want to try to open them all at once — Go will start creating more and more threads to handle the "blocked" syscalls (open(2)…

You'd probably have to be doing something pretty unusual to not use a worker queue. Your "P.S." point being a perfect case in point as to why. If you have a legitimate reason for doing something unusual, it is fine to have to use the tools unusually. It serves as a useful reminder that you are purposefully doing something unusual rather than simply making a bad design choice. A good language makes bad design decision…

Using a "work queue", i.e. a channel would still have a for loop like

  for filename := range workQueue {
      fp, err := os.Open(filename)
      if err != nil { ... }
      defer fp.Close()
      // do work
  }

Which would have the same exact problem :)

Re: Defer available in gcc and clang

#223

Earlier quoted context omitted.

You'd probably have to be doing something pretty unusual to not use a worker queue. Your "P.S." point being a perfect case in point as to why. If you have a legitimate reason for doing something unusual, it is fine to have to use the tools unusually. It serves as a useful reminder that you are purposefully doing something unusual rather than simply making a bad design choice. A good language makes bad design decision…

Using a "work queue", i.e. a channel would still have a for loop like for filename := range workQueue { fp, err := os.Open(filename) if err != nil { ... } defer fp.Close() // do work } Which would have the same exact problem :)

I don't see the problem.

    for _, filename := range files {
        queue 
or more realistically,

    var group errgroup.Group
    group.SetLimit(10)
    for _, filename := range files {
        group.Go(func() error {
            f, err := os.Open(filename)
            if err != nil {
                return fmt.Errorf("failed to open file %s: %w", filename, err)
            }
            defer f.Close()  
            // ...
            return nil          
        })
    }
    if err := group.Wait(); err != nil {
        return fmt.Errorf("failed to process files: %w", err)
    }
Perhaps you can elaborate?

I did read your code, but it is not clear where the worker queue is. It looks like it ranges over (presumably) a channel of filenames, which is not meaningfully different than ranging over a slice of filenames. That is the original, non-concurrent solution, more or less.

Re: Defer available in gcc and clang

#224

Earlier quoted context omitted.

Defer might be better than nothing, but it's still a poor solution. An obvious example of a better, structural solution is C#'s `using` blocks. using (var resource = acquire()) { } // implicit resource.Dispose(); While we don't have the same simplicity in C because we don't use this "disposable" pattern, we could still perhaps learn something from syntax and use a secondary block to have scoped defers. Something like…

That is a different approach, but I don't think you've demonstrated why it's better. Seems like that approach forces you to introduce a new scope for every resource, which might otherwise be unnecessary: using (var resource1 = acquire() { using (var resource2 = acquire()) { using (var resource3 = acquire()) { // use resources here.. } } } Compared to: var resource1 = acquire(); defer { release(resource1); } var resou…

While the macro version doesn't permit this, if it were built-in syntax (as in C#) we can write something like:

    using (auto res1 = acquire1(); free(res1))
    using (auto res2 = acquire2(); free(res2))
    using (auto res3 = acquire3(); free(res3)) 
    {
        // use resources here
    } 
    // free(res3); free(res2); free(res1); called in that order.
The argument for this approach is it is structural. `defer` statements are not structural control flow: They're `goto` or `comefrom` in disguise.

---

Even if we didn't want to introduce new scope, we could have something like F#'s `use`[1], which makes the resource available until the end of the scope it was introduced.

    use auto res1 = acquire1() defer { free(res1); };
    use auto res2 = acquire2() defer { free(res2); };
    use auto res3 = acquire3() defer { free(res3); };
    // use resources here

In either case (using or use-defer), the acquisition and release are coupled together in the code. With `defer` statements they're scattered as separate statements. The main argument for `defer` is to keep the acquisition and release of resources together in code, but defer statements fail at doing that.

[1]:https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...

Re: Defer available in gcc and clang

#225

The article is a bit dense, but what it's announcing is effectively golang's `defer` (with extra braces) or a limited form of C++'s RAII (with much less boilerplate). Both RAII and `defer` have proven to be highly useful in real-world code. This seems like a good addition to the C language that I hope makes it into the standard.

> with extra braces

The extra braces appear to be optional according to the examples in https://www.open-std.org/JTC1/SC22/WG14/www/docs/n3734.pdf (see pages 13-14)

Re: Defer available in gcc and clang

#226
post #171
post #165

Earlier quoted context omitted.

What's the use-case for block-level defer? In a tight loop you'd want your cleanup to happen after the fact. And in, say, an IO loop, you're going to want concurrency anyway, which necessarily introduces new function scope.

> In a tight loop you'd want your cleanup to happen after the fact. Why? Doing 10 000 iterations where each iteration allocates and operates a resource, then later going through and freeing those 10 000 resources, is not better than doing 10 000 iterations where each iteration allocates a resource, operates on it, and frees it. You just waste more resources. > And in, say, an IO loop, you're going to want concurrency…

[deleted]

Re: Defer available in gcc and clang

#227
post #87
post #79

Earlier quoted context omitted.

C++ implementations of defer are either really ugly thanks to using lambdas and explicitly named variables which only exist to have scoped object, or they depend on macros which need to have either a long manually namespaced name or you risk stepping on the toes of a library. I had to rename my defer macro from DEFER to MYPOROGRAM_DEFER in a project due to a macro collision. C++ would be a nicer language with native…

Because they are all the consequence of holding it wrong, avoiding RAII solutions. Working with native C APIs in C++ is akin to using unsafe in Rust, C#, Swift..., it should be wrapped in type safe functions or classes/structs, never used directly outside implementation code. If folks actually followed this more often, there would be so much less CVE reports in C++ code caused by calling into C.

> Because they are all the consequence of holding it wrong, avoiding RAII solutions.

The reason why C++ is as popular as it is is in large part due to how easy it is to upgrade an existing C codebase in-place. Doing a complete RAII rewrite is at best a long term objective, if not often completely out of the question.

Acknowledging this reality means giving affordances like `defer` that allow upgrading C codebases and C++ code written in a C style easier without having to rewrite the universe. Because if you're asking me to rewrite code in a C++ style all in one go, I might not pick C++.

EDIT: It also occurs to me that destructors also have limitations. They can't throw, which means that if you encounter an issue in a dtor you often have to ignore it and hope it wasn't important.

I ran into this particular annoyance when I was writing my own stream abstractions - I had to hope that closing the stream in the dtor didn't run into trouble.

Re: Defer available in gcc and clang

#228

Earlier quoted context omitted.

Using a "work queue", i.e. a channel would still have a for loop like for filename := range workQueue { fp, err := os.Open(filename) if err != nil { ... } defer fp.Close() // do work } Which would have the same exact problem :)

I don't see the problem. for _, filename := range files { queue or more realistically, var group errgroup.Group group.SetLimit(10) for _, filename := range files { group.Go(func() error { f, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open file %s: %w", filename, err) } defer f.Close() // ... return nil }) } if err := group.Wait(); err != nil { return fmt.Errorf("failed to process files: %w"…

I think they imagine a solution like this:

    // Spawn workers
    for _ := range 10 {
        go func() {
            for path := range workQueue {
                fp, err := os.Open(path)
                if err != nil { ... }
                defer fp.Close()
                // do work
            }
        }()
    }

    // Iterate files and give work to workers
    for _, path := range paths {
        workQueue 

Re: Defer available in gcc and clang

#229
post #228

Earlier quoted context omitted.

I don't see the problem. for _, filename := range files { queue or more realistically, var group errgroup.Group group.SetLimit(10) for _, filename := range files { group.Go(func() error { f, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open file %s: %w", filename, err) } defer f.Close() // ... return nil }) } if err := group.Wait(); err != nil { return fmt.Errorf("failed to process files: %w"…

I think they imagine a solution like this: // Spawn workers for _ := range 10 { go func() { for path := range workQueue { fp, err := os.Open(path) if err != nil { ... } defer fp.Close() // do work } }() } // Iterate files and give work to workers for _, path := range paths { workQueue

Maybe, but why would one introduce coupling between the worker queue and the work being done? That is a poor design.

Now we know why it was painful. What is interesting here is that the pain wasn't noticed as a signal that the design was off. I wonder why?

We should dive into that topic. I suspect at the heart of it lies why there is so much general dislike for Go as a language, with it being far less forgiving to poor choices than a lot of other popular languages.

Re: Defer available in gcc and clang

#230
post #228

Earlier quoted context omitted.

I think they imagine a solution like this: // Spawn workers for _ := range 10 { go func() { for path := range workQueue { fp, err := os.Open(path) if err != nil { ... } defer fp.Close() // do work } }() } // Iterate files and give work to workers for _, path := range paths { workQueue

Maybe, but why would one introduce coupling between the worker queue and the work being done? That is a poor design. Now we know why it was painful. What is interesting here is that the pain wasn't noticed as a signal that the design was off. I wonder why? We should dive into that topic. I suspect at the heart of it lies why there is so much general dislike for Go as a language, with it being far less forgiving to po…

I think your issue is that you're an architecture astronaut. This is not a compliment. It's okay for things to just do the thing they're meant to do and not be super duper generic and extensible.
Post reply on HN