【问题标题】:`_Generic` with a type as output`_Generic` 类型作为输出
【发布时间】:2021-03-10 21:03:16
【问题描述】:

_Generic 可以根据传递的变量的类型在不同的语句之间进行选择,但是(正如预期的那样)如果这些语句本身包含类型名称,它将失败。举个例子:

#define PROMOTE(var) \ 
    _Generic((var), \
        char: int);

int main() {
   char c;
   PROMOTE(c) i = 0;
   return 0;
}

人们可能期望上面的代码可以工作,使用PROMOTE 的行计算为“int i = 0”,但是很遗憾,它不能编译。我尝试了一些迂回的方法来编写类型 (int),例如使用宏 (#define TYPE_int int) 或 typedef (typedef int TYPE_int),但无济于事。这很可能是有意(或故意未定义)的行为,但我仍然对这种可能性感兴趣,即使它需要一些 C 魔法。

鉴于此,如何让_Generic 输出类型?

注意:解决方案应仅依赖于标准 C(即没有编译器特定的结构)。

【问题讨论】:

  • 至于为什么你所做的不起作用,generic association 的第二部分是assignment expression。它不像宏那样进行令牌替换。
  • 回复:“_Generic 可以在不同的语句之间切换”:不,不能。根据 C 2018 6.5.1.1 1,_Generic 在表达式(标有类型)之间进行选择。 (从技术上讲,它们必须是语法中的 assignment-expression,它只是没有逗号运算符作为其顶部运算符的任何 expression。)
  • C 标准使用Backus-Nauer form 或类似的东西来指定C 语言的语法。在各个小节中,标有“语法”的段落显示了该小节正在讨论的语法部分。 _Generic 的语法见 C 2018 6.5.1.1 1,expressionassignment-expression 的语法见 6.5.17 和 6.5.16。 int i = 0; 不是表达式或语句;这是一个声明 (6.7)。表达式也不是语句,尽管后跟分号的表达式是 (6.8.3)。
  • 6.5.1.1 中_Generic 的语法表明generic-selection_Generic ( assignment-expression @987654339 @generic-assoc-list )generic-assoc-listgeneric-association的逗号分隔列表,generic-association 是 type-name : assignment-expression。这就是我们知道_Genericassignment-expression 项之间进行选择的方式。粗略地说,assignment-expression 是对赋值运算符之一的使用,=*=/=%=+=-=<<=>>=&=^=|=。它里面没有类型……
  • i = 0 是一个赋值表达式。 i = 0; 是一个语句,它不是一个表达式。 (它确实包含一个表达式。)int i = 0; 是一个声明,它不是语句或表达式。 (它不包含表达式,除了 0 本身或语句。)_Generic 中只能有表达式,因此不能有语句或声明。

标签: c generics


【解决方案1】:

我能想象到的最接近的东西是_Generic、复合文字和typeof 扩展的组合,这些组合在流行的编译器(如 GCC 和 CLANG)中可用。

#include <stdio.h>

struct SomeStruct { int x; };

#define PROMOTE(X) typeof(_Generic((X){0}, char: (int){0}, int: (float){0}, float: (struct SomeStruct){0}))

int main() {
    PROMOTE(char) a = 1;
    PROMOTE(int) b = 2.0f;
    PROMOTE(float) c = { .x = 42 };
    printf("%d\n", a);
    printf("%f\n", b);
    printf("%d\n", c.x);
    return 0;
}

打印

1
2.000000
42

很遗憾,这不是标准 C。

【讨论】:

  • 我正在寻找一个仅依赖于标准 C 的答案(即可以在任何编译器上运行),所以我不会接受它。仍然 +1,因为您的解决方案可能对其他人有用。
  • @LuizMartins,没关系。无论如何,typeof 是最受欢迎的扩展之一。见typeof
  • 在阅读“标准已经要求typeof存在[...]任何可以处理sizeof("foo")的实现已经在内部执行sizeof(typeof("foo")),我令人费解的是,这还不是标准的一部分。谢谢你的链接。
  • @LuizMartins 标准要求typeof 不存在,您引用的页面是修辞性的
  • @M.M 那么来源错误 sizeof 运算符产生其操作数的大小(以字节为单位)[...]。大小由操作数的类型。”意味着任何可以计算sizeof("foo")的实现都必须能够计算typeof("foo")?顺便说一句,来源并没有说明标准要求 typeof 存在,而只是每个编译器已经可以以一种或另一种方式计算它,因为需要知道类型才能实现 sizeof (我的引用不是很忠实于我承认)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-09
  • 2020-03-03
  • 2016-01-07
  • 2014-12-08
  • 1970-01-01
  • 2014-09-04
  • 2020-10-14
相关资源
最近更新 更多