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

时间:2012-02-06 06:13:01

标签: c gcc types openmp typeof

有没有办法可以在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必须按照程序员最初的意图将加法运算应用于简化变量。但我的功能无法知道如何正确地做到这一点。特别是,虽然它知道操作的类型(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编写的遗留源代码。我可以使用脚本自动更改代码,但我无法将其重写为其他语言。

谢谢!

5 个答案:

答案 0 :(得分:9)

C11 _Generic

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

#include <assert.h>
#include <string.h>

#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 a.c
./a.out

可以找到大量类型的良好起点in this answer

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

答案 1 :(得分:4)

除非你编写了大量的宏,否则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(),或调用在运行时决定类型的泛型函数:

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并且进行了各种其他优化,则此运行时评估不一定​​比混淆宏慢得多,但它更安全。

答案 2 :(得分:4)

您可以使用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");

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

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

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