Hacker News new | ask | show | jobs
by jstimpfle 1619 days ago
That's a little bit like saying, it's impossible to write functioning code because you could make a mistake. But an abstraction that can be misused might still be an abstraction worth using.

Here is a macro that can be used to emulate a defer

    #define CONCAT_(a, b) a ## b
    #define CONCAT(a, b) CONCAT_(a, b)
    #define UNIQUENAME() CONCAT(i_, __LINE__)

    #define SCOPE_(counter, init_stmt, exit_stmt)  for (int counter = ((init_stmt), 1); counter--; (exit_stmt))
    #define SCOPE(init_stmt, exit_stmt) SCOPE_(UNIQUENAME(), (init_stmt), (exit_stmt))
Granted it's a hack, but it can be useful at times. I've used something like it to define a large data hierarchy in code for example, as having to close all the nodes manually is tedious.

You could wrap a second for-loop around the definition of the macro to at least be able to catch misplaced "break" statements.

1 comments

Looks very interesting. Can you please provide usage example?
Sure, you'd code like

    SCOPE(Resource *ptr = acquire_resource(),
          release_resource(ptr))
    {
        // do stuff with resource ptr.
    }
Actually, to allow variable declarations in the init_stmt like above, you'll need to use two nested for-loops:

    #define SCOPE_(name, begin_stmt, end_stmt) for (int name = 0; !name; assert(name && "should never break from a SCOPE")) for (begin_stmt; !name; name++, (end_stmt))
It is natural to add another layer of specific usage macro like this:

    #define UI_NODE(ctx) SCOPE(push_ui_node(ctx), pop_ui_node(ctx))

    UI_NODE(ctx)
    {
        ui_color(ctx, UI_COLOR_RED);

        const char *items[3] = { "a", "b", "c" };
        for (int i = 0; i < 3; i++)
        {
            UI_NODE(ctx)
                ui_text(ctx, items[i]);
        }
    }
Naturally, if you're already on C++, better code these macros in terms of RAII instead of abusing for-loops. That will add a little robustness.
I'm on C89, but this is a very useful trick. Thanks!