如何在C代码中获取变量的类型?

有没有什么方法可以在C中自动发现变量的类型,或者通过程序本身的某种机制,或者 – 更可能 – 通过使用编译器的预编译脚本传递到它的位置解析了变量并为它们分配了类型? 我正在寻找关于此的一般性建议。 下面是关于我需要什么和为什么的更多背景。

我想改变OpenMP减少子句的语义。 在这一点上,简单地通过调用函数替换源代码中的子句(通过脚本)似乎是最容易的,然后我可以定义函数来实现我想要的缩减语义。 例如,我的脚本会转换它

#pragma omp parallel for reduction(+:x) 

进入这个:

 my_reduction(PLUS, &x, sizeof(x)); #pragma omp parallel for 

在哪里,我早些时候(比方说)

 enum reduction_op {PLUS, MINUS, TIMES, AND, OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */}; 

并且my_reduction有签名

 void my_reduction(enum reduction_op op, void * var, size_t size); 

除此之外, my_reduction必须像程序员最初预期的那样将加法运算应用于简化变量。 但我的function无法知道如何正确地做到这一点。 特别是,虽然它知道操作的类型( PLUS ),原始变量的位置( var )以及变量类型的大小,但它不知道变量的类型本身。 特别是,它不知道var是否具有整数或浮点类型。 从低级POV来看,这两类类型的加法运算完全不同。

如果只有GCC支持的非标准运算符typeof将以sizeof的方式工作 – 返回某种类型的变量 – 我可以轻松地解决这个问题。 但是typeof并不像sizeof:它只能在l值声明中使用。

现在,编译器显然在完成生成可执行代码之前知道x的类型。 这让我想知道我是否可以某种方式利用GCC的解析器,只是为了得到x的类型并将其传递给我的脚本,然后再一次运行GCC来编译我改变的源代码。 那么宣布就足够简单了

 enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE}; void my_reduction(enum reduction_op op, void * var, enum var_type vtype); 

在取消引用和应用运算符之前, my_reduction可以适当地进行转换。

正如您所看到的,我正在尝试在C中创建一种“调度”机制。为什么不使用C ++重载? 因为我的项目限制我使用用C编写的遗留源代码。我可以使用脚本自动更改代码,但我无法将其重写为其他语言。

谢谢!

C11 _Generic

不是直接解决方案,但如果您耐心编码所有类型,它确实可以让您实现所需的结果,如:

 #include  #include  #define typename(x) _Generic((x), \ int: "int", \ float: "float", \ default: "other") int main(void) { int i; float f; void* v; assert(strcmp(typename(i), "int") == 0); assert(strcmp(typename(f), "float") == 0); assert(strcmp(typename(v), "other") == 0); } 

编译并运行:

 gcc -std=c11 ac ./a.out 

在这个答案中可以找到一个很好的起点。

在Ubuntu 17.10,GCC 7.2.0中测试。 GCC仅在4.9中增加了支持。

除非你编写了大量的宏,否则C在预编译时没有办法执行此操作。 我建议大量的宏方法,它基本上会这样:

 void int_reduction (enum reduction_op op, void * var, size_t size); #define reduction(type,op,var,size) type##_reduction(op, var, size) ... reduction(int, PLUS, &x, sizeof(x)); // function call 

请注意,这是非常糟糕的做法,只有在维护写得不好的遗留代码时才应该作为最后的手段,即便如此。 这种方法没有类型安全或其他此类保证。

更安全的方法是从调用者显式调用int_reduction() ,或调用在运行时决定类型的generics函数:

 void reduction (enum type, enum reduction_op op, void * var, size_t size) { switch(type) { case INT_TYPE: int_reduction(op, var, size); break; ... } } 

如果内联int_reduction并且完成了各种其他优化,则此运行时评估不一定​​比混淆宏慢得多,但它更安全。

您可以使用sizeof函数来确定类型,让未知类型的变量为var。 然后

 if(sizeof(var)==sizeof(char)) printf("char"); else if(sizeof(var)==sizeof(int)) printf("int"); else if(sizeof(var)==sizeof(double)) printf("double"); 

当两个或多个主要类型可能具有相同大小时,它会导致并发症。

您还可以考虑根据需要使用插件或MELT扩展来自定义GCC。 但是,这需要了解一些复杂的GCC内部表示(Gimple,Tree)(因此至少需要几天的工作时间)。

但是类型在C中是一个只编译的东西。它们没有具体化。

GCC提供类型的扩展。 它不是标准的,但足够常见(其他几个编译器,例如clang / llvm,有它)。

您可以考虑通过使用MELT (扩展GCC的特定于域的语言)扩展GCC以适应您的目的来定制GCC。