_Generic()中的语句而不是表达式

_Generic()中的语句而不是表达式,c,c11,C,C11,我需要在泛型中选择语句而不是表达式。我知道我可以将它们放在函数中,但我更喜欢保存函数调用并使用宏。我知道另一种可能是将语句转换为表达式的GNU扩展,但如果可能的话,我不希望使用扩展 我需要的是这样的东西: #define FLOATWORK(X) do{ \ dostuff; \ dostuff; \ dostuff;}while(0) #define DOUBLEWORK(X) do{ \ dostuff; \ dostuff; \ dostuff;}while(0) #define GE

我需要在泛型中选择语句而不是表达式。我知道我可以将它们放在函数中,但我更喜欢保存函数调用并使用宏。我知道另一种可能是将语句转换为表达式的GNU扩展,但如果可能的话,我不希望使用扩展

我需要的是这样的东西:

#define FLOATWORK(X) do{ \
dostuff; \
dostuff; \
dostuff;}while(0)

#define DOUBLEWORK(X) do{ \
dostuff; \
dostuff; \
dostuff;}while(0)

#define GENERICWORK(X) _Generic((X), float: FLOATWORK(X), double: DOUBLEWORK(X))
我想到的另一种可能性是在宏中使用逗号运算符,试图用逗号运算符将语句转换为表达式。但是,我需要宏中的switch/case,switch/case不能是AFAIK表达式


总之,我能在不将宏语句放在函数中,不使用GCC表达式语句扩展的情况下实现这一点吗?

不,你不能,因为它们需要是表达式。但是,请记住,可以使用逗号运算符和?:三元运算符和赋值可以实现许多技巧:

#include <stdio.h>

#define FLOATWORK(X) (printf("hello "), printf("world\n"), 0)

#define DOUBLEWORK(X) (X == 5.0 ? printf("It equals 5\n") : \
                                  printf("It doesn't equal 5\n"), 1)

#define F(X) _Generic((X), float: FLOATWORK(X), double: DOUBLEWORK(X))

int main(void) {
    F(5.0f);
    F(5.0);
}

不,你不能,因为它们必须是表达式。但是,请记住,可以使用逗号运算符和?:三元运算符和赋值可以实现许多技巧:

#include <stdio.h>

#define FLOATWORK(X) (printf("hello "), printf("world\n"), 0)

#define DOUBLEWORK(X) (X == 5.0 ? printf("It equals 5\n") : \
                                  printf("It doesn't equal 5\n"), 1)

#define F(X) _Generic((X), float: FLOATWORK(X), double: DOUBLEWORK(X))

int main(void) {
    F(5.0f);
    F(5.0);
}
C 2018 6.5.1.1将_Generic定义为仅将表达式作为操作数。要使用它选择语句,请使用选择语句:

switch (_Generic(X, float: 0, double: 1))
{
    case 0:
       stuff;
       break;
    case 1:
       stuff;
       break;
}
可以根据需要将其放入宏中

这使得整个结构成为一种陈述。您的问题并不排除这一点,但是,如果您希望整个构造是一个表达式,并且您想要做的“东西”也是表达式,那么您可以使用条件运算符:

_Generic(X, float: 0, double: 1) ? stuff : stuff;
C 2018 6.5.1.1将_Generic定义为仅将表达式作为操作数。要使用它选择语句,请使用选择语句:

switch (_Generic(X, float: 0, double: 1))
{
    case 0:
       stuff;
       break;
    case 1:
       stuff;
       break;
}
可以根据需要将其放入宏中

这使得整个结构成为一种陈述。您的问题并不排除这一点,但是,如果您希望整个构造是一个表达式,并且您想要做的“东西”也是表达式,那么您可以使用条件运算符:

_Generic(X, float: 0, double: 1) ? stuff : stuff;

除非你有很好的理由使用函数类宏,否则应该考虑将这些函数替换为普通函数。然后您可以创建泛型宏来创建类型泛型函数API。例如:

#include <stdio.h>
#include <stdlib.h>

float workf (float x)
{
  return x * 2.0f;
}

double worklf (double x)
{
  return x * 2.0f;
}

#define work(x) _Generic((x), float: workf, double: worklf)(x)

#define print(x) printf(_Generic((x), float: "%f\n", double: "%lf\n"), (x))

int main (void)
{
  float f = 1.0f;
  double d = 1.0;

  print(work(f));
  print(work(d));
  print(work(2.0f));
  print(work(2.0));

  return 0;
}

除非你有很好的理由使用函数类宏,否则应该考虑将这些函数替换为普通函数。然后您可以创建泛型宏来创建类型泛型函数API。例如:

#include <stdio.h>
#include <stdlib.h>

float workf (float x)
{
  return x * 2.0f;
}

double worklf (double x)
{
  return x * 2.0f;
}

#define work(x) _Generic((x), float: workf, double: worklf)(x)

#define print(x) printf(_Generic((x), float: "%f\n", double: "%lf\n"), (x))

int main (void)
{
  float f = 1.0f;
  double d = 1.0;

  print(work(f));
  print(work(d));
  print(work(2.0f));
  print(work(2.0));

  return 0;
}


可以使用三元运算符?:创建条件表达式。您可以嵌套它们以获得与switch/case等效的值。它将是不可读的,但它可能会工作。你知道函数调用非常便宜,对吗?你总是可以告诉编译器你希望函数内联。是的,如果它不能内联,那么牺牲可读性和可维护性是不值得的。你可以使用三元运算符?:创建条件表达式。您可以嵌套它们以获得与switch/case等效的值。它将是不可读的,但它可能会工作。你知道函数调用非常便宜,对吗?你总是可以告诉编译器你希望函数内联。是的,如果它不能内联,那么为获得收益而牺牲可读性和可维护性是不值得的。没错。由于该值将在编译时被检测到,所有其他情况都将得到优化。不过,正如其他人所建议的,我决定将宏转换为内联函数。由于该值将在编译时被检测到,所有其他情况都将得到优化。不过,我决定将宏转换为内联函数,正如其他人所建议的,我之所以使用宏,是因为它用于将浮点值作为参数传递给属于泛型类型接口的虚拟表的函数:每个类实际上都是struct,它是C,只有C是为float、double和long double构建的,但是,您需要使用统一的原型调用它们的方法:如何使float、double和longdouble的方法使用相同的接口?我决定将虚拟接口的参数设置为可以接受任何FP类型的标记联合。因此,我为标记的联合选择了宏作为getter/setter。现在您了解了为什么我需要switch/case:标记的联合的getter需要它。但是,setter可以使用宏来完成,实际上,setter甚至不需要逗号运算符:我使用的是一个宏,它生成一个复合文本,构建一个用于调用泛型类型的就地参数interface@cesss您可以使用上述方法构建这样一个类。虽然在不同的情况下,类不需要共存,但一个简单的typedef可以更优雅地解决所有问题。或者向前声明一个不透明的类型结构,其中包含float、double或longdouble,这就是我通常在C中实现多态性的方式
是的,它们共存是因为用户可以在运行时选择浮点精度。我通过在C语言中使用类型安全模板实现了这种多态性,如果使用include技术,则可以使用类型安全模板,对于要实例化的每个类型,都要使用一次,将模板参数T定义为每个实例化中的适当类型。但是,在某个时候,您需要一个粘合层:GUI代码需要粘合到实例化模板的代码上。我选择了一张虚拟桌子。但是,VT中的每个函数都需要相同的原型:因此标记的并集出现在游戏中。我使用宏是因为它用于将浮点值作为参数传递给属于泛型类型接口的虚拟表的函数:每个类实际上都是struct,它是C,只有C是为float、double和long double构建的,但是,您需要使用统一的原型调用它们的方法:如何使float、double和longdouble的方法使用相同的接口?我决定将虚拟接口的参数设置为可以接受任何FP类型的标记联合。因此,我为标记的联合选择了宏作为getter/setter。现在您了解了为什么我需要switch/case:标记的联合的getter需要它。但是,setter可以使用宏来完成,实际上,setter甚至不需要逗号运算符:我使用的是一个宏,它生成一个复合文本,构建一个用于调用泛型类型的就地参数interface@cesss您可以使用上述方法构建这样一个类。虽然在不同的情况下,类不需要共存,但一个简单的typedef可以更优雅地解决所有问题。或者向前声明一个不透明的类型结构,包含浮点、双精度或长双精度-这就是我在C中通常实现多态性的方式。例如,请参阅。是的,它们共存是因为用户可以在运行时选择浮点精度。我通过在C语言中使用类型安全模板实现了这种多态性,如果使用include技术,则可以使用类型安全模板,对于要实例化的每个类型,都要使用一次,将模板参数T定义为每个实例化中的适当类型。但是,在某个时候,您需要一个粘合层:GUI代码需要粘合到实例化模板的代码上。我选择了一张虚拟桌子。但是,VT中的每个函数都需要相同的原型:因此,标记的union进入了游戏。