Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace nested functions

I created a c program which uses nested functions from the gnu extension a lot and now I want to make them standard conform with ansi c.

What's the best way to transform nested functions, which access some outer vars to something different.

#define lambda(return_type, function_body) \
({ \
    return_type __fn__ function_body \
    __fn__; \
})

example usage

size_t var1;
size_t var2;
lambda(void, (...) {
    // some code
    lambda(void, (...) {
        // ...
        // do something with var1/var2
        // ..
    }

    // ...
    // do something with var1/var2
}

I thought about moving the vars to global scope, so they are known from each "lambda" which would maybe the easiest solution, but I dont want to polute the global scope and I'm not sure, if this is the cleanest way.


As asked by some commenters - here's a concrete example

/* fill itt*/
int n_method = 0;
void *add_method = lambda(void, (ir_entity *method) {
    int itable_offset = n_method++;
    const char *method_name = get_entity_name(method);

    ir_entity *implementation = get_method_entity(klass, method_name);
    if (implementation == NULL) {
        walk_up_callback(klass, lambda(bool, (ir_type *st) {
            implementation = get_method_entity(st, method_name);
            if (implementation != NULL) {
                insert_itable_method(implementation, itable_offset, interface, init);
            }
            return implementation == NULL;
        }), NULL);
    } else {
        insert_itable_method(implementation, itable_offset, interface, init);
    }
});

walk_up_callback(interface, NULL, lambda(void, (ir_type *klass) {
    if (oo_get_class_is_interface(klass)) {
        walk_table_methods_callback(add_method, klass);
    }
}));
walk_table_methods_callback(add_method, interface);

It's a part of a compiler which creates some itables for efficient interface lookups

like image 446
Philipp Avatar asked Sep 27 '22 03:09

Philipp


1 Answers

You use the callbacks for iteration through containers. If your data structure allows it, you could try to write traversal code by means of iterators, which will allow you to write what now is a separate callback as loop body.

For example, if you have a binary tree, recursive traversal with a callback looks more or less like this:

typedef struct node_t node_t;

struct node_t {
    const char *id;
    node_t *left, *right;
};

void traverse(const node_t *node, void (*func)(const node_t *n))
{
    if (node) {
        traverse(node->left, func);
        func(node);
        traverse(node->right, func);
    }
}

And it is used like so:

traverse(head, lambda(void, (const node_t *n){ puts(n->id); }));

As you have noted, in Standard C, the function must be a global function with the restriction that you can't easily and type-safely access data that is not stored in the node itself.

For a standard-compliant and more intuitive way to traverse the tree, you can rewrite the traversal as iterative code and store the state in an iterator:

typedef struct node_iter_t node_iter_t;

struct node_iter_t {
    node_t *next;
    node_t *node;
    node_t *stack[32];
    int nstack;
};

int next_node(node_iter_t *it)
{
    it->node = it->next;

    while (it->nstack || it->node) {
        while (it->node) {
            it->stack[it->nstack++] = it->node;
            it->node = it->node->left;
        }

        it->node = it->stack[--it->nstack];
        it->next = it->node->right;
        return 1;
    }

    return 0;
}

The iterator code is more verbose than the recursive traversal, but the client code is a simple loop, which can access other local variables in the function:

node_iter_t it = {head};
int i = 0;

while (next_node(&it)) {
    printf("%d: %s\n", i++, it.node->id);
}

Of course, your container might not be suited for such a rewrite.

like image 85
M Oehm Avatar answered Nov 03 '22 19:11

M Oehm