【问题标题】:Passing compound literal to _Generic not working将复合文字传递给 _Generic 不起作用
【发布时间】:2021-03-02 02:58:31
【问题描述】:

在以下代码中:

struct Person {
    char* name;
    int age;
};

struct Book {
    char* title;
    char* author;
};
#define MYTYPE(X)   _Generic((X), int: "int", float: "float", double: "double", struct Book: "book", struct Person: "person", default: "other")

以下作品:

struct Book ulysses = {"ulysses", "james"};
printf("%s\n", MYTYPE(ulysses));
struct Person jim;
jim = (struct Person) {"Tom", 20};
printf("%s\n", MYTYPE(jim));

但是,如果我尝试传递复合文字,它会失败:

printf("%s\n", MYTYPE((struct Person){"Tom", 10}));

gen.c:25:53: 错误:宏“MYTYPE”传递了 2 个参数,但只接受了 1 个
printf("%s\n", MYTYPE((struct Person){"Tom", 10}));
..................................................... .................... ^

struct Person 传递给MYTYPE 宏似乎有什么问题?


更新:似乎将表达式双重包装在括号中可以解决此问题,但我不确定为什么需要这样做:

printf("%s\n", MYTYPE(((struct Person){"Tom", 10})));

【问题讨论】:

  • @chux-ReinstateMonica 是的,有效,但我很好奇,如果我已经在函数调用中,为什么需要该部分 ()
  • 嗯......
  • 什么编译器和版本?
  • @Shawn gcc 7.5.0 (在 c 代码中执行 __VERSION__ ——这就是你的意思吗?)
  • 在 Godbolt 上玩新的 gcc 版本(和 clang)?可能是一个编译器错误,希望得到修复。

标签: c


【解决方案1】:

您使用 2 个参数调用了 MYTYPE(struct Person){"Tom"10}。与括号不同,大括号在预处理器级别没有语法意义,并且不会抑制逗号作为宏参数分隔符的作用。您需要将复合文字括起来以避免这种情况。或者,在某些情况下(包括您的情况),您可以使用...__VA_ARGS__ 来制作一个避免该问题的可变参数宏。另请参阅我的问题:Compound literals and function-like macros: bug in gcc or the C standard?

【讨论】:

  • 谢谢,这是一个很好的解释。您能否解释一下... 将如何成为解决问题的另一种方法?
  • @carl.hiass:如果宏只接受可变参数 ... 参数,__VA_ARGS__ 将扩展为由逗号分隔的整个参数集,因此它将“重新组装”一个复合文字被分成多个论点。我忘记了使用没有非可变参数的... 在技术上是否是有效的 C,但我知道的所有编译器都支持它,我认为它是。
【解决方案2】:

您遇到的问题是由于预处理器语法。 MACRONAME(a,b) 表示使用两个参数 ab 调用 MACRONAME,而不管 a 、逗号和 b 的组合是否恰好形成了一些语义上有效的表达式。 (在预处理阶段,我们还没有将标记排列成表达式)。

例外情况与字符串文字和匹配的括号对有关:

  • 字符串文字中的逗号不是参数分隔符
  • 匹配的括号对中的逗号不是分隔符。 (这指的是参数列表中的括号,而不是宏替换语法中的括号)。

因此双括号起作用的原因:MACRONAME((a,b)) 表示使用 (a,b) 的单个参数调用 MACRONAME

【讨论】:

  • 澄清一下,OP 的宏调用有 2 个参数:(struct Person){"Tom"10}
  • @M.M.您能解释一下a comma inside a matched pair of parentheses is not a separator. 的工作原理吗?这是否意味着除了函数调用本身之外还有括号?所以...function((a,b)) 而不是 function(a,b)
  • @carl.hiass 是的,我在最后一段中给出了这个例子。这些也不是函数调用,它们是宏替换。 MACRONAME( argument-list ) 是宏替换调用的语法。这些括号不是参数列表的一部分
【解决方案3】:
typedef struct Person {
    char* name;
    int age;
}      t_Person; // (sorry I took this habit)

请注意,如果您声明它,则不再需要双重包装:

t_Person a = {"Tom", 10};
printf("%s\n", MYTYPE((((t_Person){"Tom", 10})))); -> works
printf("%s\n", MYTYPE(a)); -> works, saves 4 parenthesis.

我的猜测是 MYTYPE 不知道自己内部是什么,因为 _Generic 它可能是 int、float 等...所以编译器认为括号之间的结构实际上是一个强制转换而不是声明,添加另一个括号解决了冲突。

我可以证明:

#define MYTYPE(X)   _Generic((X),  char: "char", int: "int", float: "float", double: "double", struct Book: "book", struct Person: "person", default: "other")

printf("%s\n", MYTYPE((int){'a'})); // -> output int
printf("%s\n", MYTYPE((char){'a'})); // -> output char

First, the type of controlling-expression undergoes lvalue conversions。转换仅在类型域中执行:它丢弃顶级 cvr 限定符和原子性,并将数组到指针/函数到指针的转换应用于控制表达式的类型,而不启动任何副作用或计算任何值。

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 2019-05-26
    • 2012-09-05
    • 2015-10-30
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多