Replace nested functions

I created a c program that uses a lot of nested functions from the gnu extension, and now I want them to conform to the ansi c standard.

What is the best way to convert nested functions that access some external vars to something else.

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

usage example

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

I was thinking about moving vars to a global scope, so they are known from every “lambda”, which may be the easiest solution, but I don't want to use a global scope, and I'm not sure if this is the cleanest way.


As some commentators asked, here is 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); 

This is the part of the compiler that creates some itables for efficient interface lookups.

+5
source share
2 answers

You use callbacks to iterate through containers. If your data structure allows this, you can try writing crawl code using iterators, which allows you to write what is now a separate callback as the body of the loop.

For example, if you have a binary tree, a recursive traversal with a callback looks something 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 this:

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

As you already noted, in the C standard, a function must be a global function with a restriction that you cannot easily and safely print with data that is not stored in the node itself.

For a standard and more intuitive way to navigate the tree, you can rewrite the traversal as an iterative code and save 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; } 

Iterator code is more verbose than recursive traversal, but client code is a simple loop that can access other local variables in a 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 may not be suitable for such rewriting.

+2
source

The cleanest, most idiomatic way, in my opinion, is to make simple non-nested functions that take all the variables that they need as arguments, either by value if they are input, or through a pointer if outputs.

If this becomes difficult due to the number of variables, this is probably a sign of a more serious problem in the code and more substantial refactoring is likely to be required.

Consider putting groups of closely related variables in structures, if you want to reduce the number of random free variables in your code, this is probably also more expressive. Using built-in functions is usually a very untidy encoding method, as it contributes to large, obscure areas.

However, macros are likely to be even worse, since they will prevent any possibility that the compiler avoids repeating the code of nested functions and further pollutes the external scope with any variables defined in the nested function.

Globals are arguably the worst solution for everyone, as they expand the reach of the entire program and are also significantly slower to access and significantly slow down your code. They will also make collision of variable names almost inevitable in a larger program.

+2
source

All Articles