在C99 / C11中使用预处理器嵌套goto标签?

时间:2012-06-24 11:03:19

标签: c

是否可以使用C预处理器在C11或C99中嵌套转到标签?通过查看以下代码可能最好地说明了我的情况。使用gcc -std=c99 -pedantic -Wall -Wextra干净地编译。

#include <stdio.h>

// Macro for mangling the identifier to avoid collisions
#define CTX_ID_(NAME) context_label_ ## NAME ## _
#define CTX_ID(NAME) CTX_ID_(NAME)

// The context keyword starts a block that can be exited with break (ID);
// Just syntactic sugar to keep it structured.
#define context(ID) \
    if (0) { CTX_ID(ID): ; } else

// Overloaded break keyword. Doesn't prevent using the plain break;    
#define break(ID) \
    do { goto CTX_ID(ID); } while (0)

// Example run
int main(void) {
    context (c) {
        while (1) {
            puts("Outer loop, visible.");
            while (1) {
                puts("Inner loop, visible.");
                break (c);
                puts("You won't see me.");
            }
        }
        puts("Nor me.");
    }
}

我试图取消标识符(在本例中为c)。但是,与变量不同,goto标签不能嵌套/作用域,因为它们必须在函数中是唯一的。是否可以在C预处理器中实现可用作goto标签的唯一作用域标识符?

GCC支持获取标签的地址,但它不是ISO标准的一部分。另外,由于开销和易失性问题,我特意试图避免setjmp。最后,如果您没有看到上述构造的有用性,请考虑进一步使用,如try-catch子句或Python样式的with-expressions,以启用类似RAII的功能。

2 个答案:

答案 0 :(得分:2)

我确定 __LINE__宏可能会派上用场。没有范围,但至少你可以用这种方式生成唯一的标签名称。

然而,这并不是很明显,这也将解决您的问题。我将大胆并声明它不可解决,虽然我确定有可能有人会出现并证明我错了!

答案 1 :(得分:1)

你的想法很好看,我唯一想念的是你的break(c)可以在函数的任何地方发布。我会在这两个宏中添加类似的东西:

#define CONTEXT(ID)                                     \
    if (0) { CTX_ID(ID): ; }                            \
    else for (register bool CTX_ID(ID ## ID) = true;    \
              CTX_ID(ID ## ID);                         \
              CTX_ID(ID ## ID) = false)

#define BREAK(ID)                \
    do {                         \
       CTX_ID(ID ## ID) = false; \
       goto CTX_ID(ID);          \
   } while (0)

如果在依赖块之外使用BREAK(c),则会导致语法错误。根据我的经验,这里使用的for变量很容易被现代编译器优化掉。