2012-02-06 13 views
6

¿Hay alguna manera de que pueda descubrir el tipo de una variable automáticamente en C, ya sea a través de algún mecanismo dentro del programa o, más probablemente, a través de una compilación previa? secuencia de comandos que utiliza los pases del compilador hasta el punto en que ha analizado las variables y les ha asignado sus tipos? Estoy buscando sugerencias generales sobre esto. A continuación hay más antecedentes sobre lo que necesito y por qué.Obteniendo el tipo de variable en el código C

Me gustaría cambiar la semántica de la cláusula de reducción OpenMP. En este punto, parece más fácil simplemente reemplazar la cláusula en el código fuente (a través de un script) con una llamada a una función, y luego puedo definir la función para implementar la semántica de reducción que quiero. Por ejemplo, mi guión sería convertir esta

#pragma omp parallel for reduction(+:x) 

en esto:

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

donde, antes, tengo (digamos)

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

Y my_reduction tiene la firma

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

Entre otras t hings, my_reduction tendría que aplicar la operación de adición a la variable de reducción como el programador había pensado originalmente. Pero mi función no puede saber cómo hacer esto correctamente. En particular, aunque conoce el tipo de operación (PLUS), la ubicación de la variable original (var) y el tamaño del tipo de la variable, no conoce el tipo de la variable en sí. En particular, no sabe si var tiene un tipo integral o de coma flotante. Desde un POV de bajo nivel, la operación de adición para esas dos clases de tipos es completamente diferente.

Si solo el operador no estándar typeof, que es compatible con GCC, funcionara como funciona sizeof - devolviendo algún tipo de variable de tipo - podría resolver este problema fácilmente. Pero typeof no es realmente como sizeof: solo se puede usar, al parecer, en declaraciones l-value.

Ahora, el compilador obviamente sabe el tipo de x antes de que termine de generar el código ejecutable. Esto me lleva a preguntarme si de alguna manera puedo aprovechar el analizador de GCC, solo para obtener el tipo x y pasarlo a mi secuencia de comandos, y luego ejecutar GCC nuevamente, hasta el final, para compilar mi código fuente alterado. Entonces sería lo suficientemente simple para declarar

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

Y my_reduction puede lanzar de forma adecuada antes de eliminación de referencias y aplicando el operador.

Como puede ver, estoy tratando de crear un tipo de mecanismo de "despacho" en C. ¿Por qué no usar solo la sobrecarga de C++? Porque mi proyecto me obliga a trabajar con código fuente heredado escrito en C. Puedo modificar el código automáticamente con un script, pero no puedo reescribirlo en otro idioma.

Gracias!

+2

¿Qué hay de post-procesamiento de su código fuente con algunas herramientas/scripts? P.ej. analizarlo con clang, encontrar los tipos, insertar/ajustar el código específico del tipo, compilar? –

+0

Gracias, Alex. Parece que va en la dirección correcta. –

+1

Leí en alguna parte que la reducción definida por el usuario será parte del estándar 3.1 o 4.0. Hm 3.1 dice: 'reduction ({operator | intrinsic_procedure_name}: list)' ..never tried intrinsic_procedure_name. aunque solo resuelve parcialmente tu detección de tipo. – Bort

Respuesta

2

GCC proporciona la extensión typeof. No es estándar, pero es lo suficientemente común (varios otros compiladores, por ejemplo, clang/llvm, lo tienen).

Quizás pueda considerar personalizar GCC extendiéndolo con MELT (un lenguaje específico de dominio para extender GCC) para que se ajuste a sus propósitos.

+1

Gracias, Basile. Como puede ver en mi publicación, conozco typeof, pero typeof no me dará lo que necesito: no devuelve nada; no puede pasarlo o su información a una función. Puede hacer esto: #define add (x, y, z) {\ typeof (x) _x = x; \ typeof (y) _y = y; \ z = _x + _y; \ } Me parece que el operador typeof tiene una utilidad muy limitada. –

+1

No es estándar y no funcionará en la mayoría de los compiladores de C. En mi experiencia, es una muy mala idea usar extensiones GCC para código de producción. – Lundin

2

También podría considerar personalizar GCC con un complemento o una extensión MELT para sus necesidades. Sin embargo, esto requiere comprender algunas de las representaciones internas de GCC (Gimple, Tree) que son complejas (por lo que le tomará al menos días de trabajo).

Pero los tipos son solo una cosa de compilación en C. No están reificados.

+1

Esto es más o menos lo que terminé haciendo, no es un complemento, sino un código pirateado dentro de GCC. Descubrí cómo obtener la información de tipo de los Gimple tree_nodes en tiempo de compilación. Tomó un poco de excavación. ¡Gracias! –

4

C realmente no tiene una manera de realizar esto en el tiempo de precompilación, a menos que escriba una avalancha de macros. Me no recomienda la inundación de enfoque macros, sería básicamente algo así:

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 

Tenga en cuenta que esto es muy mala práctica y sólo se debe utilizar como último recurso cuando el mantenimiento de código heredado mal escrito, si aún entonces. No hay seguridad de tipo u otras garantías similares con este enfoque.

Un enfoque más seguro es llamar explícitamente int_reduction() de la persona que llama, o para llamar a una función genérica que decide el tipo en tiempo de ejecución:

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

Si int_reduction se colocarán en línea y varias otras optimizaciones se realizan, esta evaluación en tiempo de ejecución no es necesariamente mucho más lento que las macros ofuscadas, pero es mucho más seguro.

+1

Sí, gracias por tus pensamientos. ¿Pero cómo averiguas el tipo? Ese es mi gran desafío. El resto es solo sintaxis, y por supuesto estoy de acuerdo con sus comentarios sobre la mejor sintaxis. En el caso anterior, por ejemplo, ¿cómo sé que una variable determinada es INT_TYPE? La secuencia de comandos que estoy escribiendo para mover la reducción "reducción (+: x)" a una llamada aparte "(INT_TYPE, PLUS, & var, sizeof (x));" Tendría que saber que x tiene tipo INT_TYPE, pero ¿cómo? - ¡menos que escribir un analizador completo solo para obtener el tipo de x? –

+1

@AmittaiAviram ¿Cómo sabes cuándo declarar una variable como 'int'? No tengo idea de la naturaleza de sus datos ni de dónde proceden, así que no puedo responder esa pregunta. Si se trata de algún tipo de datos brutos externos, entonces, naturalmente, tendrá que determinar el tipo de datos antes de hacer cualquier cálculo. Y ese es un problema de algoritmo que no tiene nada que ver con la sintaxis de programación C como tal. – Lundin

+1

@ Lundin - No, no. La imagen es más como esto. Supongamos que tiene una función 'main' que tiene la declaración' int x' cerca de la parte superior. Digamos 100 líneas abajo de eso, tienes '#pragma omp parallel para reducción (+: x)'. La 'x' allí tiene el tipo' int' porque así es como se declaró en el alcance actual. El programador lo sabe y podría anotar una llamada a la función por sí mismo, pero quiero poder hacer esto automáticamente. –

7

C11 _Generic no es una solución directa, pero sí permite que usted pueda lograr el resultado deseado si usted es paciente para codificar todos los tipos como en:

#define typename(x) _Generic((x), \ 
    int:  "int", \ 
    float: "float", \ 
    default: "other") 

int i; 
float f; 
void* v; 
assert(strcmp(typename(i), "int") == 0); 
assert(strcmp(typename(f), "float") == 0); 
assert(strcmp(typename(i), "other") == 0); 

Un buen punto de partida con un montón de tipos se puede encontrar in this answer.

GCC solo agregó soporte en 4.9.

4

Puede usar la función sizeof para determinar el tipo, deje que la variable de tipo desconocido sea var. continuación

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

Tú Será llevado a complicaciones cuando dos o más tipos primarios podrían tener mismo tamaño.

Cuestiones relacionadas