Je travaille donc sur une conception logicielle utilisant C pour un certain processeur. La trousse d'outils comprend la possibilité de compiler C ainsi que C++. Pour ce que je fais, il n'y a pas d'allocation de mémoire dynamique disponible dans cet environnement et le programme est globalement assez simple. Sans oublier que l'appareil n'a presque pas de puissance ni de ressources processeur. Il n'y a vraiment aucun besoin fort d'utiliser n'importe quel C++.
Cela étant dit, il y a quelques endroits où je fais une surcharge de fonctions (une caractéristique de C++). J'ai besoin d'envoyer quelques types de données différents et je n'ai pas envie d'utiliser la mise en forme de style printf
avec une sorte de %s
(ou autre) argument. J'ai vu des gens qui n'avaient pas accès à un compilateur C++ faire la chose printf
, mais dans mon cas, le support C++ est disponible.
Maintenant, je suis sûr que je pourrais avoir la question de savoir pourquoi je dois surcharger une fonction pour commencer. Je vais donc essayer de répondre à cela tout de suite. J'ai besoin de transmettre différents types de données sur un port série, j'ai donc quelques surcharges qui transmettent les types de données suivants:
unsigned char*
const char*
unsigned char
const char
Je préférerais simplement ne pas avoir une méthode qui gère toutes ces choses. Lorsque j'appelle la fonction, je veux juste qu'elle transmette le port série, je n'ai pas beaucoup de ressources, donc je ne veux pas faire à peine QUOI QUE CE SOIT mais ma transmission.
Quelqu'un d'autre a vu mon programme et m'a demandé: "Pourquoi utilisez-vous des fichiers CPP?" C'est ma seule raison. Est-ce une mauvaise pratique?
Mise à jour
Je voudrais répondre à quelques questions posées:
Une réponse objective à votre dilemme dépendra:
Si la taille de l'exécutable augmente considérablement si vous utilisez C++.
À l'heure actuelle, la taille de l'exécutable consomme 4,0% de la mémoire du programme (sur 5248 octets) et 8,3% de la mémoire des données (sur 342 octets). Autrement dit, la compilation pour C++ ... Je ne sais pas à quoi cela ressemblerait pour C parce que je n'ai pas utilisé le compilateur C. Je sais que ce programme ne se développera plus, alors pour les ressources limitées, je dirais que je vais bien là-bas ...
S'il y a un impact négatif notable sur les performances si vous utilisez C++.
Eh bien, s'il y en a, je n'ai rien remarqué ... mais là encore, c'est peut-être pourquoi je pose cette question car je ne comprends pas bien.
Si le code peut être réutilisé sur une plate-forme différente où seul un compilateur C est disponible.
Je sais que la réponse à cette question est définitivement non . Nous envisageons en fait de passer à un processeur différent, mais seulement à des processeurs ARM plus puissants (dont tous je sais pertinemment qu'ils ont des chaînes d'outils de compilation C++).
Je n'irais pas jusqu'à qualifier cela de "mauvaise pratique" en soi, mais je ne suis pas non plus convaincu que ce soit vraiment la bonne solution à votre problème. Si tout ce que vous voulez, c'est quatre fonctions distinctes pour faire vos quatre types de données, pourquoi ne pas faire ce que les programmeurs C ont fait depuis des temps immémoriaux:
void transmit_uchar_buffer(unsigned char *buffer);
void transmit_char_buffer(char *buffer);
void transmit_uchar(unsigned char c);
void transmit_char(char c);
C'est effectivement ce que le compilateur C++ fait dans les coulisses de toute façon et ce n'est pas un gros problème pour le programmeur. Évite tous les problèmes de "pourquoi écrivez-vous pas-tout-C avec un compilateur C++", et signifie que personne d'autre sur votre projet ne sera confondu par quels bits de C++ sont "autorisés" et quels bits ne le sont pas.
Utiliser seulement certaines fonctionnalités de C++ tout en le traitant autrement comme C n'est pas exactement commun, mais pas non plus exactement non plus. En fait, certaines personnes utilisent même aucune fonctionnalité du tout C++, à l'exception de la vérification de type plus stricte et plus puissante. Ils écrivent simplement C (en prenant soin d'écrire uniquement dans l'intersection commune de C++ et C), puis compilent avec un compilateur C++ pour la vérification de type, et un compilateur C pour la génération de code (ou simplement s'en tenir à un compilateur C++ jusqu'au bout).
Linux est un exemple de base de code où les gens demandent régulièrement que les identifiants comme class
soient renommés klass
ou kclass
, afin de pouvoir compiler Linux avec un compilateur C++. De toute évidence, étant donné l'opinion de Linus sur C++ , ils sont toujours abattus :-D GCC est un exemple d'une base de code qui a d'abord été transformée en "C++ - clean C", et puis progressivement refactorisé pour utiliser plus de fonctionnalités C++.
Il n'y a rien de mal à ce que vous faites. Si vous êtes vraiment paranoïaque à propos de la qualité de génération de code de votre compilateur C++, vous pouvez utiliser un compilateur comme Comeau C++, qui compile en C comme langage cible, puis utiliser le compilateur C. De cette façon, vous pouvez également effectuer des inspections ponctuelles du code pour voir si l'utilisation de C++ injecte un code imprévu sensible aux performances. Cela ne devrait pas être le cas pour la simple surcharge, qui génère littéralement automatiquement des fonctions différemment nommées - IOW, exactement ce que vous feriez en C de toute façon.
Une réponse objective à votre dilemme dépendra:
Si la réponse à l'une des questions est "oui", il serait préférable de créer des fonctions nommées différemment pour différents types de données et de rester avec C.
Si la réponse à toutes les questions est "non", je ne vois aucune raison pour laquelle vous ne devriez pas utiliser C++.
Vous posez la question comme si la compilation avec C++ vous donnait simplement accès à plus de fonctionnalités. Ce n'est pas le cas. Compiler avec un compilateur C++ signifie que votre code source est interprété comme une source C++, et C++ est un langage différent de C. Les deux ont un sous-ensemble commun assez grand pour être utilement programmé, mais chacun a des fonctionnalités qui l'autre manque et il est possible d'écrire du code acceptable dans les deux langues mais interprété différemment.
Si vraiment tout ce que vous voulez, c'est la surcharge de fonctions, je ne vois vraiment pas l'intérêt de confondre le problème en y intégrant le C++. Au lieu de différentes fonctions portant le même nom, distinguez leurs listes de paramètres, écrivez simplement des fonctions avec des noms différents.
Quant à vos critères objectifs,
- Si la taille de l'exécutable augmente considérablement si vous utilisez C++.
L'exécutable peut être légèrement plus grand lorsqu'il est compilé en C++, par rapport à un code C similaire construit en tant que tel. Au minimum, l'exécutable C++ aura l'avantage douteux de la manipulation de nom C++ pour tous les noms de fonction, dans la mesure où tous les symboles sont conservés dans l'exécutable. (Et c'est en fait ce qui prévoit vos surcharges en premier lieu.) Si la différence est suffisamment grande pour être importante pour vous, c'est quelque chose que vous devrez déterminer par expérience.
- S'il y a un impact négatif notable sur les performances si vous utilisez C++.
Je doute que vous constatiez une différence de performance notable pour le code que vous décrivez par rapport à un hypothétique analogique pur-C.
- Si le code peut être réutilisé sur une plate-forme différente où seul un compilateur C est disponible.
Je jetterais une lumière légèrement différente à ce sujet: si vous voulez lier le code que vous construisez avec C++ au code autre, alors cet autre code devra également être écrit en C++ et construit avec C++, ou vous aurez besoin de faire une disposition spéciale dans votre propre code (déclarant la liaison "C"), ce que, en outre, vous ne pouvez pas faire du tout pour vos fonctions surchargées. D'un autre côté, si votre code est écrit en C et compilé en C, d'autres peuvent le lier aux modules C et C++. Ce type de problème peut généralement être surmonté en tournant les tables, mais comme vous ne semblez vraiment pas avoir besoin de C++, alors pourquoi accepter un tel problème en premier lieu?
À l'époque, l'utilisation d'un compilateur C++ comme "un meilleur C" était promue comme cas d'utilisation. En fait, le début de C++ était exactement cela. Le principe de conception sous-jacent était que vous ne pouviez utiliser que les fonctionnalités que vous vouliez et n'encourrait pas le coût des fonctionnalités que vous n'utilisiez pas. Ainsi, vous pourriez surcharger les fonctions (en déclarant l'intention via le mot clé overload
!) Et le reste de votre projet non seulement compilerait très bien mais générerait du code pas pire que le compilateur C produirait.
Depuis lors, les langues ont quelque peu divergé.
Chaque malloc
dans votre code C sera une erreur de non-correspondance de type - pas un problème dans votre cas sans mémoire dynamique! De même, tous vos pointeurs void
en C vous feront trébucher, car vous devrez ajouter des transtypages explicites. Mais… pourquoi le faites-vous de cette façon … vous serez amené de plus en plus à utiliser les fonctionnalités C++.
Donc, cela pourrait être possible avec un peu de travail supplémentaire. Mais il servira de passerelle vers une adoption à plus grande échelle du C++. Dans quelques années, les gens se plaindront de votre code hérité qui semble avoir été écrit en 1989, car ils remplacent vos appels malloc
par new
, extraient des blocs de code de corps de boucle pour simplement appeler un algorithme à la place, et réprimons le faux polymorphisme dangereux qui aurait été trivial si le compilateur avait été autorisé à le faire.
D'un autre côté, vous savez que ce serait la même chose si vous l'aviez écrit en C, est-il donc toujours mal d'écrire en C au lieu de C++ étant donné son existence? Si la réponse est "non", alors l'utilisation de fonctionnalités sélectionnées en C++ ne peut pas être erronée non plus.
Le polymorphisme est une très bonne fonctionnalité que C++ fournit gratuitement. Cependant, vous devrez peut-être prendre en compte d'autres aspects lors du choix d'un compilateur. Il existe une alternative au polymorphisme en C, mais son utilisation peut provoquer des sourcils surélevés. C'est la fonction variadique, veuillez vous référer à Tutoriel sur la fonction variadique .
Dans votre cas, ce serait quelque chose comme
enum serialDataTypes
{
INT =0,
INT_P =1,
....
}Arg_type_t;
....
....
void transmit(Arg_type_t serial_data_type, ...)
{
va_list args;
va_start(args, serial_data_type);
switch(serial_data_type)
{
case INT:
//Send integer
break;
case INT_P:
//Send data at integer pointer
break;
...
}
va_end(args);
}
J'aime l'approche Phillips mais elle encombre votre bibliothèque avec beaucoup d'appels. Avec ce qui précède, l'interface est propre. Il a ses inconvénients et, finalement, c'est une question de choix.
De nombreux langages de programmation ont une ou plusieurs cultures qui se développent autour d'eux et ont des idées particulières sur ce qu'un langage est censé être. Bien qu'il devrait être possible, pratique et utile d'avoir un langage de bas niveau adapté à la programmation de systèmes qui augmente un dialecte de C adapté à cet effet avec certaines fonctionnalités de C++ qui seraient également appropriées, les cultures entourant les deux langues ne se t particulièrement favorable à une telle fusion.
J'ai lu un compilateur C intégré qui incorporait certaines fonctionnalités de C++, y compris la possibilité d'avoir
foo.someFunction(a,b,c);
interprété comme essentiellement
typeOfFoo_someFunction(foo, a, b, c);
ainsi que la possibilité de surcharger les fonctions statiques (les problèmes de changement de nom ne surviennent que lorsque les fonctions exportées sont surchargées). Il n'y a aucune raison pour que les compilateurs C ne soient pas en mesure de prendre en charge une telle fonctionnalité sans imposer des exigences supplémentaires à l'environnement de liaison et d'exécution, mais le rejet réflexif de nombreux compilateurs C de presque tout de C++ dans le but d'éviter les charges environnementales les amène à rejeter même les fonctionnalités qui n'imposeraient pas un tel coût. Pendant ce temps, la culture du C++ s'intéresse peu à tout environnement qui ne peut pas prendre en charge toutes les fonctionnalités de ce langage. À moins que ou jusqu'à ce que la culture de C change pour accepter de telles fonctionnalités, ou que la culture de C++ change pour encourager les implémentations de sous-ensembles légers, le code "hybride" est susceptible de souffrir de nombreuses limitations des deux langages tout en étant limité dans sa capacité à récolter les avantages de fonctionner dans un surensemble combiné.
Si une plate-forme prend en charge C et C++, le code de bibliothèque supplémentaire requis pour prendre en charge C++ rendra probablement l'exécutable un peu plus grand, bien que l'effet ne soit pas particulièrement important. L'exécution des "fonctionnalités C" dans C++ ne sera probablement pas particulièrement affectée. Un problème plus important peut être la manière dont les optimiseurs C et C++ gèrent divers cas d'angle. Le comportement des types de données dans un C est principalement défini par le contenu des bits qui y sont stockés, et C++ a une catégorie reconnue de structures (PODS - Plain Old Data Structures) dont la sémantique est également principalement définie par les bits stockés. Cependant, les normes C et C++ autorisent parfois un comportement contraire à celui impliqué par les bits stockés, et les exceptions au comportement des "bits stockés" diffèrent entre les deux langages. La programmation des systèmes nécessite souvent des garanties comportementales au-delà de celles spécifiées par les deux langages, mais en raison de la différence entre les cultures de C et C++, les implémentations des deux langages peuvent offrir des garanties différentes.
Un autre facteur important à considérer: celui qui héritera de votre code.
Cette personne sera-t-elle toujours un programmeur C avec un compilateur C? Je suppose.
Cette personne sera-t-elle également un programmeur C++ avec un compilateur C++? Je voudrais être raisonnablement sûr de cela avant de faire dépendre mon code de choses spécifiques à C++.
Est-ce une mauvaise pratique d'utiliser un compilateur C++ juste pour la surcharge de fonctions?
Du point de vue IMHO, oui, et je devrai devenir schizophrène pour répondre à celle-ci car j'aime les deux langues mais cela n'a rien à voir avec l'efficacité, mais plus comme la sécurité et l'utilisation idiomatique des langues.
Côté C
D'un point de vue C, je trouve tellement inutile de faire en sorte que votre code nécessite C++ juste pour utiliser la surcharge de fonctions. À moins que vous ne l'utilisiez pour le polymorphisme statique avec des modèles C++, c'est un sucre syntaxique trivial gagné en échange du passage à un langage entièrement différent. De plus, si vous souhaitez exporter vos fonctions vers un dylib (cela peut ou non être une préoccupation pratique), vous ne pouvez plus le faire de manière très pratique pour une consommation généralisée avec tous les symboles à nom modifié.
Côté C++
D'un point de vue C++, vous ne devriez pas utiliser C++ comme C avec surcharge de fonction. Il ne s'agit pas d'un dogmatisme stylistique mais lié à l'utilisation pratique du C++ de tous les jours.
Votre type normal de code C n'est raisonnablement sain et "sûr" à écrire que si vous travaillez contre le système de type C qui interdit des choses comme copier des cteurs dans structs
. Une fois que vous travaillez dans le système de type beaucoup plus riche de C++, les fonctions quotidiennes qui ont une valeur énorme comme memset
et memcpy
ne deviennent pas des fonctions sur lesquelles vous devriez vous appuyer tout le temps. Au lieu de cela, ce sont des fonctions que vous voulez généralement éviter comme la peste, car avec les types C++, vous ne devriez pas les traiter comme des bits et des octets bruts à copier et à mélanger et à libérer. Même si votre code utilise uniquement des choses comme memset
sur les primitives et les UDT POD pour le moment, au moment où quelqu'un ajoute un ctor à n'importe quel UDT que vous utilisez (y compris en ajoutant simplement un membre qui en requiert un, comme std::unique_ptr
membre) contre de telles fonctions ou une fonction virtuelle ou quoi que ce soit de ce genre, il rend tout votre codage de style C normal susceptible de comportement indéfini. Prenez-le de Herb Sutter lui-même:
memcpy
etmemcmp
violent le système de types. Utilisermemcpy
pour copier des objets, c'est comme gagner de l'argent en utilisant une photocopieuse. Utilisermemcmp
pour comparer des objets, c'est comme comparer des léopards en comptant leurs taches. Les outils et les méthodes peuvent sembler faire le travail, mais ils sont trop grossiers pour le faire de manière acceptable. Les objets C++ concernent la dissimulation d'informations (sans doute le principe le plus rentable en génie logiciel; voir le point 11): les objets cachent les données (voir le point 41) et conçoivent des abstractions précises pour copier ces données via des constructeurs et des opérateurs d'affectation (voir les points 52 à 55) . Bulldozer sur tout cela avecmemcpy
est une violation grave de la dissimulation d'informations, et conduit souvent à des fuites de mémoire et de ressources (au mieux), des plantages (pire) ou un comportement indéfini (pire) - C++ Coding Standards.
Tant de développeurs C seraient en désaccord avec raison, car la philosophie ne s'applique que si vous écrivez du code en C++. Vous êtes très probablement écrivez du code très problématique si vous utilisez des fonctions comme memcpy
tout le temps dans le code qui se construit en C++ , mais c'est très bien si vous le faites en C . Les deux langues sont très différentes à cet égard en raison des différences dans le système de type. Il est très tentant de regarder le sous-ensemble de fonctionnalités que ces deux ont en commun et de croire que l'une peut être utilisée comme l'autre, en particulier du côté C++, mais le code C + (ou code C--) est généralement beaucoup plus problématique que C et Code C++.
De même, vous ne devriez pas utiliser, par exemple, malloc
dans un contexte de style C (qui n'implique aucun EH) s'il peut appeler directement des fonctions C++ qui peuvent lancer, car vous avez alors un point de sortie implicite dans votre fonction en raison de l'exception que vous ne pouvez pas capturer efficacement en écrivant du code de style C, avant de pouvoir free
cette mémoire. Donc, chaque fois que vous avez un fichier qui se construit en C++ avec un .cpp
extension ou autre chose et il fait tous ces types de choses comme malloc
, memcpy
, memset
, qsort
, etc., alors il demande des problèmes plus loin si ce n'est déjà fait, à moins que ce soit le détail d'implémentation d'une classe qui ne fonctionne qu'avec des types primitifs, auquel cas il doit encore faire la gestion des exceptions pour être à l'abri des exceptions. Si vous écrivez du code C++, vous souhaitez plutôt compter généralement sur RAII et utiliser des choses comme vector
, unique_ptr
, shared_ptr
, etc., et évitez autant que possible le codage de style C normal.
La raison pour laquelle vous pouvez jouer avec des lames de rasoir dans les types de données C et à rayons X et jouer avec leurs bits et octets sans être enclin à causer des dommages collatéraux dans une équipe (bien que vous puissiez toujours vous blesser de toute façon) n'est pas à cause de ce que C types peuvent faire, mais à cause de ce qu'ils ne pourront jamais faire . Au moment où vous étendez le système de types de C pour inclure des fonctionnalités C++ comme les ctors, les dtors et les vtables, ainsi que la gestion des exceptions, tout le code C idiomatique serait rendu bien plus dangereux qu'il ne l'est actuellement, et vous verrez un nouveau type de la philosophie et la mentalité évoluent, ce qui encouragera un style de codage complètement différent, comme vous le voyez en C++, qui envisage désormais même d'utiliser une mauvaise pratique de pointeur brut pour une classe qui gère la mémoire par opposition, disons, à une ressource conforme à RAII comme
unique_ptr
. Cet état d'esprit n'est pas né d'un sentiment absolu de sécurité. Il a évolué à partir de ce dont C++ a spécifiquement besoin pour être protégé contre des fonctionnalités telles que la gestion des exceptions, étant donné ce qu'il permet simplement via son système de types.
Exception-sécurité
Encore une fois, dès que vous serez en terre C++, les gens s'attendront à ce que votre code soit protégé contre les exceptions. Les gens pourraient maintenir votre code à l'avenir, étant donné qu'il est déjà écrit et compilé en C++, et simplement utiliser std::vector, dynamic_cast, unique_ptr, shared_ptr
, etc. dans du code appelé directement ou indirectement par votre code, le jugeant inoffensif puisque votre code est déjà "supposément" du code C++. À ce stade, nous devons faire face à la chance que les choses se lancent, puis lorsque vous prenez un code C parfaitement fin et charmant comme ceci:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
... il est maintenant cassé. Il doit être réécrit pour être sûr d'exception:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
Brut! C'est pourquoi la plupart des développeurs C++ l'exigeraient à la place:
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
Ce qui précède est du code RAII conforme aux exceptions, du type que les développeurs C++ approuveraient généralement car la fonction ne révélera aucune ligne de code qui déclenche une sortie implicite à la suite d'un throw
.
Choisissez une langue
Vous devez soit adopter le système de type et la philosophie de C++ avec RAII, la sécurité d'exception, les modèles, la POO, etc., soit embrasser C qui tourne largement autour des bits et octets bruts. Vous ne devriez pas former un mariage impie entre ces deux langues, et plutôt les séparer en langues distinctes à traiter très différemment au lieu de les brouiller ensemble.
Ces langues veulent vous épouser. Vous devez généralement en choisir un au lieu de sortir ensemble et de vous amuser avec les deux. Ou vous pouvez être un polygame comme moi et vous marier tous les deux, mais vous devez complètement changer d'avis lorsque vous passez du temps l'un avec l'autre et les garder bien séparés les uns des autres afin qu'ils ne se battent pas.
taille binaire
Juste par curiosité, j'ai essayé de prendre mon implémentation de liste gratuite et de comparer maintenant et de le porter en C++ depuis que je suis vraiment curieux à ce sujet:
[...] je ne sais pas à quoi cela ressemblerait pour C parce que je n'ai pas utilisé le compilateur C.
... et je voulais savoir si la taille binaire se gonflerait du tout en construisant en C++. Cela m'a obligé à saupoudrer des castes explicites partout, ce qui était fugace (une raison pour laquelle j'aime vraiment écrire des choses de bas niveau comme les allocateurs et les structures de données en C mieux), mais cela n'a pris qu'une minute.
Il s'agissait simplement de comparer une version MSVC 64 bits pour une application console simple et avec du code qui n'utilisait aucune fonctionnalité C++, pas même une surcharge d'opérateur - juste la différence entre la construire avec C et utiliser, disons, <cstdlib>
au lieu de <stdlib.h>
et des choses comme ça, mais j'ai été surpris de constater que cela ne faisait aucune différence avec la taille binaire!
Le binaire était 9,728
octets lors de sa construction en C, et de même 9,278
octets lors de la compilation en code C++. En fait, je ne m'attendais pas à ça. Je pensais que des choses comme EH y ajouteraient au moins un peu (je pensais que ce serait au moins comme une centaine d'octets différents), bien qu'il ait probablement pu comprendre qu'il n'était pas nécessaire d'ajouter des instructions liées à EH car je suis juste en utilisant la bibliothèque standard C et rien ne lève. J'ai pensé que quelque chose ajouterait un peu à la taille binaire de toute façon, comme RTTI. Quoi qu'il en soit, c'était plutôt cool de voir ça. Bien sûr, je ne pense pas que vous devriez généraliser à partir de ce seul résultat, mais cela m'a au moins un peu impressionné. Cela n'a également eu aucun impact sur les références, et naturellement, puisque j'imagine que la taille binaire résultante identique signifiait également des instructions machine résultantes identiques.
Cela dit, qui se soucie de la taille binaire avec les problèmes de sécurité et d'ingénierie mentionnés ci-dessus? Encore une fois, choisissez une langue et adoptez sa philosophie au lieu d'essayer de la bâtarder; c'est ce que je recommande.
Pour votre cas particulier de surcharge statique d'une "fonction" pour quelques types différents, vous pourriez plutôt envisager d'utiliser C11 avec sa machine _Generic
. Je pense que cela pourrait suffire pour vos besoins limités.
En utilisant la réponse de Philip Kendall vous pouvez définir:
#define transmit(X) _Generic((X), \
unsigned char*: transmit_uchar_buffer, \
char*: transmit_char_buffer, \
unsigned char: transmit_uchar, \
char: transmit_char) ((X))
et le code transmit(foo)
quel que soit le type de foo
(parmi les quatre types listés ci-dessus).
Si vous ne vous souciez que des compilateurs GCC (et compatibles, par exemple Clang ), vous pouvez considérer ses __builtin_types_compatible_p
avec son extension typeof
.