2

我正在尝试看看我可以在多大程度上滥用 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

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

} } else { if (x) {

由于else被定义。

有什么方法可以在不被预处理器拾取的情况下elif使用“原始” ?else我想我需要尝试嵌套多个定义来欺骗预处理器直接粘贴单词而不解析它,但我不知道如何实现这一点。

有什么想法,或者这在 GCC 中是不可能的吗?

编辑:

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

#define A B
#define B C

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

4

2 回答 2

4

更新

我想我设法解决了。我利用了:

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,它都可以在有或没有其他情况下工作。

于 2021-12-10T09:10:55.940 回答
2

基于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    }
于 2021-12-10T23:05:06.120 回答