Hacker News new | ask | show | jobs
by eps 1609 days ago
> block scoped defer

That's the thing. Block-scoped is a better option as far as the language "spirit" is concerned, but it's limiting (see below). Function-scoped is more useful, but when used in loops it may lead to unbound stack usage and that sorta goes against the rest of C, because no other _language construct_ comes with such lovely side effect.

Re: limiting - It's not uncommon for a function to need to grab some resource conditionally and then use it in the rest of the function code, e.g.

    void foo()
    {
        bar * b = NULL;
        if (x && y)
        {
            this();
            b = that();
        }
        ...
        baz(1, 2, b); // b may be null
        ...
        release(b);
    }
This can't be handled with block-scope defers. This needs function-scoped ones.

A better option would (probably) be to allow binding defers to a specific on-stack variable... but that's basically a destructor and that opens its own can of worms, not all of which as technical.

1 comments

It seems a bit limiting, yes, but this does not seem like a major limitation to me. Especially if we compare it to how existing practice with goto based cleanup handlers would work in this example. It doesn't really matter that the resource was obtained in a block, the variable holding a reference is still scoped to the function body and will be checked at the end just as it would be with goto.

    void foo()
    {
        bar * b = NULL;
        defer [&]{if (b) release(b);}

        if (x && y)
        {
            this();
            b = that();
        }

        if (something_gone_wrong())
        {
            return; // no problem, b gets released if it was acquired
        }
        ...
        baz(1, 2, b); // b may be null
    }
If making the release conditional seems a bit hacky, remember that you need that sort of thing anyway for the hugely common case where you allocate & initialize a bunch of things and then let the caller keep the resources, except if there's an error.. in which case you need to clean everything up. Without some additional language features (first class error types or "error returns", then error defers?) these conditions are unavoidable.
Sticking defer under the var declaration is clever, but it doesn't look an improvement in terms of the code quality to me. It trades verbosity of the "out:" pattern for the need to register the cleanup code before the acquisition code. That's just weird. It's not complicated, just... backwards. Almost like a solution in search of a problem :)
Dunno, I feel like the goto out pattern is substantially more irritating any time you actually want to return a value from the function. I'd like to just return val instead of int val; /* ... */ ret=val; goto out;