禁止 C 预处理器在另一个中使用宏

Disallow C preprocessor from using a macro within another

我正在尝试看看我可以滥用 C 预处理器到什么程度,我偶然发现了一个有趣的问题。

我有以下宏定义:

#define if(x)   if (x)
#define do      {
#define elif(x) } else if (x) {
#define else    } else {
#define done    }

哪个应该允许我写:

if (i == 1)
do
    ...
elif (i == 2)
    ...
else
    ...
done

如果我只使用 ifelse,它工作得很好,除了引入 elif 是有问题的,因为宏扩展为:

} } else { if (x) {

由于 else 被定义。

有什么方法可以让 elif 使用 'raw' else 而无需预处理器接收它?我想我需要尝试嵌套多个定义来欺骗预处理器直接粘贴单词而不解析它,但我不确定如何实现这一点。

任何想法,或者这在 GCC 中是不可能的吗?

编辑:

本质上,这可以归结为以下问题:

#define A B
#define B C

对于给定的两个定义 AB,我怎样才能让 A 仍然解析为字面词 B 而不是通过第二个定义并最终成为 C ?

更新

我想我设法解决了它。我利用了那个:

if(x) {
    ...
} 

相同
for(; x ;) {
    ...
    break:
}

我们需要的是保存x的结果。我们不能重用它,因为 x 可能是一个有副作用的表达式。所以:

int b;

for(; b = (x);) {

    break;
}

现在,我们可以检查b看上面的for循环是否被执行了。使用 for 循环完成的完整 if-elif-else 模式如下所示:

for(;b = (x);) { // if
    ...
    break; 
} 

for(; !b ? b=(x==1) : 0;) { // elif
    ...
    break; 
} 

for(; !b ;) { // else
    ...
    break; 
}

有了这个,我们可以像这样把它包起来,但要注意。但是,如果您在循环中执行 if(x) break,这将无法正常工作。见下文。

int b; // Store truth value of last if or elif

#define if(x)   for(;b = !!(x);)
#define do      {
#define elif(x) break; }  for(; !b ? b=!!(x) : 0;) {
#define else    break; }  for(;!b;) { 
#define done    break; }

演示:https://onlinegdb.com/Zq6Y7vm5Q

没有 break 语句的替代方法:

int b; // Store truth value of last if or elif

#define if(x)   for(int c=1 ; c && (b = !!(x)); c=0)
#define do      {
#define elif(x) }  for(int c=1; c && (!b ? b=!!(x) : 0); c=0) {
#define else    }  for(int c=1; c && !b; c=0) { 
#define done    }

但是请注意,如果您在其中有这样的 break 语句,则这两种方法都可能会失败:

for(...) {
    if(x)
    do
        break;
    done
}

因为那会扩展到:

for(...) {
    for(int c=1 ; c && (b = !!(x)); c=0)
    {
        break;
    }
}

注:

这应该是显而易见的,但如果您决定使用此代码(不),则使用比 bc 更好的名称以避免冲突。

旧的解决方法

不完全符合您的要求,但您已经承认您基本上只是在滥用预处理器。 :)

但一个简单的解决方法是使用 else.

的同义词
#define if(x)      if (x)
#define do         {
#define elif(x)    } else if (x) {
#define otherwise  } else {
#define done       }

演示:https://onlinegdb.com/Cp-gYpOvm

它适用于零个、一个或多个 elif 实例,无论有多少个 elif,它都可以使用或不使用其他方式。

基于 klutt 的想法,这里有一个不需要额外变量的替代方案,但仅限于单个 elif 子句:

#define if(x)   switch (!!(x))
#define do      { case 1: {
#define elif(x) } break; default: switch (!!(x)) { case 1:
#define else    } break; default: {
#define done    }}

除了 elif 缺点之外,此解决方案不能很好地与用户编写的循环和开关结合使用。这些案例将被错误处理:

for(...) {
    if(...) break;
}

switch (x) {
  case 0:
    if (...) break;
    else { y = 42; break; }
  ...
}

为了您的目的,使用大写伪关键字似乎是更好的方法(并且故意看起来很丑)。

#define IF(x)   if (x)
#define DO      {
#define ELIF(x) } else if (x) {
#define ELSE    } else {
#define DONE    }