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

时间:2022-03-29 23:19:10

Is there any way that I can discover the type of a variable automatically in C, either through some mechanism within the program itself, or--more likely--through a pre-compilation script that uses the compiler's passes up to the point where it has parsed the variables and assigned them their types? I'm looking for general suggestions about this. Below is more background about what I need and why.

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

I would like to change the semantics of the OpenMP reduction clause. At this point, it seems easiest simply to replace the clause in the source code (through a script) with a call to a function, and then I can define the function to implement the reduction semantics I want. For instance, my script would convert this

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

#pragma omp parallel for reduction(+:x)

into this:

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

where, earlier, I have (say)

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

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

And my_reduction has signature

并且my_reduction有签名

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

Among other things, my_reduction would have to apply the addition operation to the reduction variable as the programmer had originally intended. But my function cannot know how to do this correctly. In particular, although it knows the kind of operation (PLUS), the location of the original variable (var), and the size of the variable's type, it does not know the variable's type itself. In particular, it does not know whether var has an integral or floating-point type. From a low-level POV, the addition operation for those two classes of types is completely different.

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

If only the nonstandard operator typeof, which GCC supports, would work the way sizeof works--returning some sort of type variable--I could solve this problem easily. But typeof is not really like sizeof: it can only be used, apparently, in l-value declarations.

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

Now, the compiler obviously does know the type of x before it finishes generating the executable code. This leads me to wonder whether I can somehow leverage GCC's parser, just to get x's type and pass it to my script, and then run GCC again, all the way, to compile my altered source code. It would then be simple enough to declare

现在,编译器显然在完成生成可执行代码之前知道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);

And my_reduction can cast appropriately before dereferencing and applying the operator.

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

As you can see, I am trying to create a kind of "dispatching" mechanism in C. Why not just use C++ overloading? Because my project constrains me to work with legacy source code written in C. I can alter the code automatically with a script, but I cannot rewrite it into a different language.

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

Thanks!

5 个解决方案

#1


8  

C11 _Generic

Not a direct solution, but it does allow you to achieve the desired result if you are patient to code all types as in:

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

#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);
}

Compile and run with:

编译并运行:

gcc -std=c11 a.c
./a.out

A good starting point with tons of types can be found in this answer.

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

Tested in Ubuntu 17.10, GCC 7.2.0. GCC only added support in 4.9.

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

#2


4  

C doesn't really have a way to perform this at pre-compile time, unless you write a flood of macros. I would not recommend the flood of macros approach, it would basically go like this:

除非你编写了大量的宏,否则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

Note that this is very bad practice and should only be used as last resort when maintaining poorly written legacy code, if even then. There is no type safety or other such guarantees with this approach.

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

A safer approach is to explicitly call int_reduction() from the caller, or to call a generic function which decides the type in runtime:

更安全的方法是从调用者显式调用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;
    ...
  }
} 

If int_reduction is inlined and various other optimizations are done, this runtime evaluation isn't necessarily that much slower than the obfuscated macros, but it is far safer.

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

#3


4  

You can use sizeof function to determine type , let the variable of unknown type be var. then

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

Thou it will led to complications when two or more primary types might have same size .

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

#4


2  

You could also consider customizing GCC with a plugin or a MELT extension for your needs. However, this requires understanding some of GCC internal representations (Gimple, Tree) which are complex (so will take you days of work at least).

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

But types are a compile-only thing in C. They are not reified.

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

#5


2  

GCC provides the typeof extension. It is not standard, but common enough (several other compilers, e.g. clang/llvm, have it).

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

You could perhaps consider customizing GCC by extending it with MELT (a domain specific language to extend GCC) to fit your purposes.

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

#1


8  

C11 _Generic

Not a direct solution, but it does allow you to achieve the desired result if you are patient to code all types as in:

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

#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);
}

Compile and run with:

编译并运行:

gcc -std=c11 a.c
./a.out

A good starting point with tons of types can be found in this answer.

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

Tested in Ubuntu 17.10, GCC 7.2.0. GCC only added support in 4.9.

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

#2


4  

C doesn't really have a way to perform this at pre-compile time, unless you write a flood of macros. I would not recommend the flood of macros approach, it would basically go like this:

除非你编写了大量的宏,否则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

Note that this is very bad practice and should only be used as last resort when maintaining poorly written legacy code, if even then. There is no type safety or other such guarantees with this approach.

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

A safer approach is to explicitly call int_reduction() from the caller, or to call a generic function which decides the type in runtime:

更安全的方法是从调用者显式调用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;
    ...
  }
} 

If int_reduction is inlined and various other optimizations are done, this runtime evaluation isn't necessarily that much slower than the obfuscated macros, but it is far safer.

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

#3


4  

You can use sizeof function to determine type , let the variable of unknown type be var. then

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

Thou it will led to complications when two or more primary types might have same size .

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

#4


2  

You could also consider customizing GCC with a plugin or a MELT extension for your needs. However, this requires understanding some of GCC internal representations (Gimple, Tree) which are complex (so will take you days of work at least).

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

But types are a compile-only thing in C. They are not reified.

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

#5


2  

GCC provides the typeof extension. It is not standard, but common enough (several other compilers, e.g. clang/llvm, have it).

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

You could perhaps consider customizing GCC by extending it with MELT (a domain specific language to extend GCC) to fit your purposes.

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