'How to get the type of a variable in C code?
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.
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
#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
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.
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.
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
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.
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.
Thanks!
Solution 1:[1]
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.
Solution 2:[2]
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:
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:
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.
Solution 3:[3]
You can use sizeof function to determine type , let the variable of unknown type be var. then
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 .
Solution 4:[4]
GCC provides the typeof extension. It is not standard, but common enough (several other compilers, e.g. clang/llvm, have it).
You could perhaps consider customizing GCC by extending it with MELT (a domain specific language to extend GCC) to fit your purposes.
Solution 5:[5]
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).
But types are a compile-only thing in C. They are not reified.
Solution 6:[6]
In general it is not possible to identify what kind of data is in a given byte or sequence of bytes. For example, the 0 byte could be an empty string or the integer 0. the bit pattern for 99 could be that number, or the letter 'c'.
The following is a bit of hackery to turn an arbitrary sequence of bytes into a printable value. It works in most cases (but not for numbers that could also be characters). It is for the lcc compiler under Windows 7, with 32-bit ints, longs and 64-bit doubles.
char* OclAnyToString(void* x)
{ char* ss = (char*) x;
int ind = 0;
int* ix = (int*) x;
long* lx = (long*) x;
double* dx = (double*) x;
char* sbufi = (char*) calloc(21, sizeof(char));
char* sbufl = (char*) calloc(21, sizeof(char));
char* sbufd = (char*) calloc(21, sizeof(char));
if (ss[0] == '\0')
{ sprintf(sbufi, "%d", *ix);
sprintf(sbufd, "%f", *dx);
if (strcmp(sbufi,"0") == 0 &&
strcmp(sbufd,"0.000000") == 0)
{ return "0"; }
else if (strcmp(sbufd,"0.000000") != 0)
{ return sbufd; }
else
{ return sbufi; }
}
while (isprint(ss[ind]) && 0 < ss[ind] && ss[ind] < 128 && ind < 1024)
{ /* printf("%d\n", ss[ind]); */
ind++;
}
if (ss[ind] == '\0')
{ return (char*) x; }
sprintf(sbufi, "%d", *ix);
sprintf(sbufl, "%ld", *lx);
sprintf(sbufd, "%f", *dx);
if (strcmp(sbufd,"0.000000") != 0)
{ free(sbufi);
free(sbufl);
return sbufd;
}
if (strcmp(sbufi,sbufl) == 0)
{ free(sbufd);
free(sbufl);
return sbufi;
}
else
{ free(sbufd);
free(sbufi);
return sbufl;
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Lundin |
| Solution 3 | parth_07 |
| Solution 4 | |
| Solution 5 | Basile Starynkevitch |
| Solution 6 | Kevin Lano |
