替换嵌套函数

时间:2015-08-04 06:39:48

标签: c c99 nested-function

我创建了一个c程序,它使用了gnu扩展中的嵌套函数,现在我想使它们符合ansi c标准。

什么是转换嵌套函数的最佳方法,嵌套函数将一些外部变量访问到不同的东西。

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

示例用法

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

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

我考虑过将变速器移到全球范围内,因此从每个" lambda"这可能是最简单的解决方案,但我不想要考虑全球范围,我不确定,如果这是最干净的方式。

正如一些评论者所说 - 这是一个具体的例子

/* 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);

它是编译器的一部分,它为有效的接口查找创建了一些itables

2 个答案:

答案 0 :(得分:2)

在我看来,最简洁,最惯用的方法是制作简单的非嵌套函数,它们将所需的所有变量作为参数,如果它们是输入则通过值,或者如果输出则通过指针。

如果由于变量的数量而变得困难,这可能是代码中更大问题的迹象,并且可能需要更多实质性的重构。

如果要降低代码中随机松散变量的数量,请考虑将密切相关的变量组放入结构中,这可能也更具表现力。使用内联函数通常是一种非常不整齐的编码方法,因为它会促进大范围的,不明确的范围。

然而,宏可能是更糟糕的解决方案,因为它们将阻止编译器避免重复嵌套函数代码的任何可能性,并且将使用嵌套函数中定义的任何变量更多地污染外部作用域。

Globals可能是最糟糕的解决方案,因为它们将范围扩展到整个程序,此外访问速度也大大降低,并且会极大地降低代码速度。在更大的程序中,它们也会使变量名冲突几乎不可避免。

答案 1 :(得分:2)

您可以使用回调来迭代容器。如果你的数据结构允许它,你可以尝试通过迭代器编写遍历代码,这将允许你编写现在作为循环体的单独回调。

例如,如果你有一个二叉树,带回调的递归遍历看起来或多或少是这样的:

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);
    }
}

它的用法如下:

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

正如您所指出的那样,在标准C中,该函数必须是一个全局函数,其限制是您不能轻松且类型安全地访问未存储在节点本身中的数据。

对于符合标准且更直观的遍历树的方法,您可以将遍历重写为迭代代码并将状态存储在迭代器中:

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;
}

迭代器代码比递归遍历更冗长,但客户端代码是一个简单的循环,可以访问函数中的其他局部变量:

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

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

当然,您的容器可能不适合这样的重写。