45 votes

C++ - Qu'est-ce qu'une "classe transparente au type" ?

En utilisant gcc pour compiler un programme qui inclut le support des types de données décimales, j'ai récemment rencontré l'erreur suivante :

error: type transparent class 'std::decimal::decimal32' has base classes

Un rapide coup d'œil à l'arbre des sources de GCC montre que ce message d'erreur se trouve dans gcc/cp/class.c .

Qu'est-ce qu'une "classe de type transparent" ? Pourquoi est-ce une erreur qu'une telle classe ait des "classes de base" ?

21voto

rodrigo Points 34500

En lisant un peu plus le code source de GCC, en semantics.c :

  if (TREE_CODE (t) == RECORD_TYPE
      && !processing_template_decl)
    {
      tree ns = TYPE_CONTEXT (t);
      if (ns && TREE_CODE (ns) == NAMESPACE_DECL
          && DECL_CONTEXT (ns) == std_node
          && DECL_NAME (ns)
          && !strcmp (IDENTIFIER_POINTER (DECL_NAME (ns)), "decimal"))
        {
          const char *n = TYPE_NAME_STRING (t);
          if ((strcmp (n, "decimal32") == 0)
              || (strcmp (n, "decimal64") == 0)
              || (strcmp (n, "decimal128") == 0))
            TYPE_TRANSPARENT_AGGR (t) = 1;
        }
    }

Ce code signifie qu'un type est marqué transparent si :

  • Il s'agit d'une structure, mais pas d'un modèle ;
  • Et c'est au niveau de l'espace de nom, et cet espace de nom est std::decimal .
  • Et il s'appelle decimal32 , decimal64 o decimal128 .

Sur class.c il y a le contrôle d'erreur que vous avez rencontré, et quelques autres.

Et dans mangle.c :

      /* According to the C++ ABI, some library classes are passed the
         same as the scalar type of their single member and use the same
         mangling.  */
      if (TREE_CODE (type) == RECORD_TYPE && TYPE_TRANSPARENT_AGGR (type))
        type = TREE_TYPE (first_field (type));

Le commentaire est la clé ici. Je pense que cela signifie qu'un type transparent est remplacé par le type de son premier (et seul) membre, et qu'il peut donc être utilisé partout où son premier membre le peut. Par exemple, dans mon include/decimal la classe std::decimal::decimal32 a un seul champ de type __decfloat32 (d'un précédent typedef float __decfloat32 __attribute__((mode(SD))); ), donc toute fonction qui prend un __decfloat32 peut prendre un std::decimal::decimal32 et vice-versa. Même la décoration des fonctions est faite de la même manière. L'idée est probablement de rendre l'ABI de ces classes compatible avec les types C. _Decimal32 , _Decimal64 y _Decimal128 .

Maintenant, comment obtenez-vous un class decimal32 avec les classes de base ? Ma seule hypothèse est que vous incluez des fichiers d'en-tête incompatibles (peut-être plus anciens), avec une implémentation totalement différente.

UPDATE

Après quelques recherches, il semble que ma supposition sur l'ABI et la décoration des fonctions soit juste. Le code suivant :

#include <decimal/decimal>
using namespace std::decimal;

 //This is a synonym of C99 _Decimal32, but that is not directly available in C++
typedef float Decimal32 __attribute__((mode(SD)));

void foo(decimal32 a) {}
void foo(Decimal32 a) {}

donne la curieuse erreur :

/tmp/ccr61gna.s: Assembler messages:
/tmp/ccr61gna.s:1291: Error: symbol `_Z3fooDf' is already defined

C'est-à-dire que le compilateur frontal ne voit aucun problème dans la surcharge et émet le code asm, mais comme les deux fonctions sont décorées de la même façon, l'assembleur échoue.

Maintenant, est-ce une non-conformité de GCC, comme le suggère Ben Voigt dans les commentaires ? Je ne sais pas... vous devriez être capable d'écrire des fonctions surchargées avec les deux types différents que vous voulez. Mais d'un autre côté, il est impossible de faire en sorte que la fonction Decimal32 sans utiliser une extension du compilateur, donc la signification de ce type est définie par l'implémentation...

11voto

Macmade Points 27414

Comme mentionné dans un de mes commentaires, un type-transparent est une classe emballage à un type primitif, comme les entiers, etc.

Ils sont appelés transparent en raison de leur utilisation de surcharge des opérateurs ce qui les fait agir exactement comme le type primitif qu'ils enveloppent.

IE, pour envelopper un int de manière transparente dans une classe, vous devrez surcharger la fonction = l'opérateur, le ++ opérateur, etc...

Apparemment, Libstdc++ de GNU utilise de telles classes pour certains types. Je ne sais pas trop pourquoi...

A propos de la problème de la classe de base Bien que je ne sois pas sûr à 100%, voici une supposition.

Lorsque vous traitez de l'héritage en C++, vous aurez souvent besoin de déclarer virtuel pour résoudre les problèmes de upcasting .

Déclarer une méthode comme virtuel indiquera au compilateur de créer un table virtuelle pour les méthodes, afin qu'elles puissent être consultées au moment de l'exécution.
Cela va bien sûr augmenter la taille de l'instance de la classe.

Pour une classe transparente en termes de type, cela n'est pas acceptable, car le compilateur ne pourra pas placer une instance d'une telle classe dans un fichier enregistrez (c'est-à-dire lors du passage d'arguments, etc.), contrairement au type enveloppé et donc la classe ne sera plus transparente.

Modifier

Je n'ai aucune idée de comment déclarer une telle classe transparente dans GCC. La chose la plus proche à laquelle je peux penser est syndicats transparents :

http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html

Quelque chose comme :

class IntWrapper
{
    int _x;

    /* Constructor, operator overloads... */
};

typedef union
{
    int        integerValue;
    IntWrapper integerWrapper;
}
IntUnion __attribute__( ( __transparent_union__ ) );

Ma version de GCC ne semble pas le supporter, mais selon la documentation (voir le lien ci-dessus), cela permettrait de int o IntWrapper pour être passés aux fonctions de manière transparente en utilisant la même convention d'appel que int .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X