Comment obtenir le type d’une variable en code C?

Existe-t-il un moyen de découvrir automatiquement le type d’une variable en C, via un mécanisme du programme même ou, plus vraisemblablement, via un script de pré-compilation utilisant les passes du compilateur jusqu’au point où a analysé les variables et leur a atsortingbué leurs types? Je cherche des suggestions générales à ce sujet. Vous trouverez ci-dessous plus d’informations sur ce dont j’ai besoin et pourquoi.

J’aimerais changer la sémantique de la clause de réduction OpenMP. À ce stade, il semble plus simple de remplacer simplement la clause dans le code source (via un script) par un appel à une fonction, puis de définir la fonction pour implémenter la sémantique de réduction que je souhaite. Par exemple, mon script convertirait ceci

#pragma omp parallel for reduction(+:x) 

dans ceci:

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

où, plus tôt, j’ai (dis)

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

Et my_reduction a la signature

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

Entre autres choses, my_reduction devrait appliquer l’opération d’addition à la variable de réduction comme le programmeur l’avait initialement prévu. Mais ma fonction ne sait pas comment faire cela correctement. En particulier, bien qu’il connaisse le type d’opération ( PLUS ), l’emplacement de la variable d’origine ( var ) et la taille du type de la variable, il ne connaît pas le type de la variable lui-même. En particulier, il ne sait pas si var est de type intégral ou à virgule flottante. À partir d’un POV de bas niveau, l’opération d’addition de ces deux classes de types est complètement différente.

Si seul le type d’opérateur non standard, typeof charge par GCC, fonctionnerait de la même façon que sizeof – renvoyant une sorte de variable de type – je pourrais résoudre ce problème facilement. Mais typeof n’est pas vraiment semblable à sizeof: il ne peut être utilisé, apparemment, que dans des déclarations à valeur l.

Maintenant, le compilateur connaît évidemment le type de x avant de finir de générer le code exécutable. Cela m’amène à me demander si je peux utiliser d’une manière ou d’une autre l’parsingur de GCC, juste pour obtenir le type de x et le transmettre à mon script, puis pour exécuter à nouveau GCC, à fond, afin de comstackr mon code source modifié. Il serait alors assez simple de déclarer

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

Et my_reduction peut effectuer un casting approprié avant de déréférencer et d’appliquer l’opérateur.

Comme vous pouvez le constater, j’essaie de créer une sorte de mécanisme de “répartition” en C. Pourquoi ne pas simplement utiliser la surcharge C ++? Parce que mon projet me contraint à travailler avec du code source hérité écrit en C. Je peux modifier le code automatiquement à l’aide d’un script, mais je ne peux pas le réécrire dans un autre langage.

Merci!

C11 _Generic

Ce n’est pas une solution directe, mais cela vous permet d’obtenir le résultat souhaité si vous êtes patient pour coder tous les types comme dans:

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

Comstackr et exécuter avec:

 gcc -std=c11 ac ./a.out 

Vous trouverez dans cette réponse un bon sharepoint départ avec des tonnes de types.

Testé sous Ubuntu 17.10, GCC 7.2.0. GCC n’a ajouté que le support en 4.9.

C n’a pas vraiment le moyen de le faire au moment de la pré-compilation, à moins d’écrire un flot de macros. Je ne recommanderais pas l’approche du stream de macros, cela ressemblerait en gros à ceci:

 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 

Notez que ceci est une très mauvaise pratique et ne devrait être utilisé qu’en dernier recours pour conserver un code hérité mal écrit, même si. Il n’ya pas de type sécurité ou autre garantie de ce type avec cette approche.

Une approche plus sûre consiste à appeler explicitement int_reduction() partir de l’appelant ou à appeler une fonction générique qui décide du type au moment de l’exécution:

 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 est en ligne et que diverses autres optimisations sont effectuées, cette évaluation d’exécution n’est pas nécessairement beaucoup plus lente que les macros obfusquées, mais elle est beaucoup plus sûre.

Vous pouvez utiliser la fonction sizeof pour déterminer le type, laissez la variable de type inconnu être var. puis

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

Cela entraînera des complications lorsque deux types primaires ou plus pourraient avoir la même taille.

Vous pouvez également envisager de personnaliser GCC avec un plugin ou une extension MELT selon vos besoins. Cependant, cela nécessite de comprendre certaines des représentations internes de GCC (Gimple, Tree) qui sont complexes (cela vous prendra au moins des jours de travail).

Mais les types sont une chose uniquement compilée en C. Ils ne sont pas réifiés.

GCC fournit l’extension typeof . Ce n’est pas standard, mais assez commun (plusieurs autres compilateurs, par exemple clang / llvm, l’ont).

Vous pourriez peut-être envisager de personnaliser GCC en l’étendant avec MELT (un langage spécifique au domaine pour étendre GCC) afin de l’adapter à vos besoins.