IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Ada Discussion :

Equivalence "union" en Ada


Sujet :

Ada

  1. #1
    Candidat au Club
    Inscrit en
    Mai 2007
    Messages
    6
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 6
    Points : 4
    Points
    4
    Par défaut Equivalence "union" en Ada
    Bonjour à tous,

    je suis sur un projet dans lequel j'ai besoin de réaliser un protocole embarqué.

    Du coup je voudrais utiliser une structure (pour mettre mes données) et ensuite "mapper" cette structure dans un tableau d'octets (pour les envoyer sur le réseau). Mais je n'y arrive pas sans avoir deux allocations mémoires, ce qui dans l'embarqué me pose de gros problèmes...

    Un petit exemple valant mieux qu'un long discours, voici le problème :

    Déjà le .ads :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
     
    type T_Request_Type is (Protocol_Frame, Protocol_Stream);
     
    type Bytes is mod 256; -- modular type
      for Bytes'Size use 8; -- 8 bits represented
    type T_Request_Byte_Array is array (positive range 1 .. 864) of Bytes;
     
    type T_Mon_Protocole_Frame is
          record
    	 Champ1  : Integer;
    	 Champ2  : Integer;
    	 Champ3  : Integer;
          end record;
     
    type T_MonProtocole_Request (Request_Kind : T_Request_Type := Protocol_Frame) is
          record
    	 case Request_Kind is
    	 when Protocol_Frame =>
    	    Trame_Construite : T_Mon_Protocole_Frame;
    	 when Protocol_Stream =>
    	    Trame_Serialized : T_Request_Byte_Array;
    	 end case;
          end record;
    Ensuite le .adb :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    with Mon_Protocole;
    use Mon_Protocole;
     
    procedure adamain is
       type T_Req_Ptr is access all T_MonProtocole_Request (Request_Kind => Protocol_Stream);
       Test_Requete : T_MonProtocole_Request;
       Test2 : aliased T_MonProtocole_Request(Request_Kind => Protocol_Stream);
       Ptr_Message : T_Req_Ptr;
    begin
       Test_Requete.Trame_Construite.Champ1 := 1;
       Test_Requete.Trame_Construite.Champ2 := 25;
       Test_Requete.Trame_Construite.Champ3 := 2;
     
       Test2 := Test_Requete;
       Ptr_Message := Test2'access;
     
    end adamain;
    Voilà. Mon soucis c'est que je suis obligé de créer un objet non-mutable et statique (Test2) afin d'avoir un pointeur sur ma structure.

    Ce que j'aurais aimé, c'est avoir Test_Requete positionné avec les bons champs et ensuite d'avoir un pointeur de type T_Request_Byte_Array qui pointe sur le début de ma structure (un peu comme les unions en C).

    J'espère que c'est assez clair, je suis moi-même un peu perdu

    Si vous avez une idée n'hésitez surtout pas !

    Merci.

  2. #2
    Membre habitué
    Inscrit en
    Décembre 2004
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 119
    Points : 156
    Points
    156
    Par défaut
    D'abord, un truc, utiliser le type standard Integer pour des donnees n'est pas forcement une bonne idee, car tu n'as aucune garantie de la taille qui sera utilisee par le compilo (tu a une borne inf, mais pas une taille exacte, meme si la majorite des implementations utilisent 4 octets, tu n'en aucune garantie).
    Donc comme tu l'as fait pour l'octet, je te conseille de creer ton type et de fournir l'attribut 'Size.

    Deuxio, sans clause de representation, tu risques d'avoir des surprises avec ton type à discriminant.

    Ensuite, pourquoi as-tu besoin d'un pointeur exactement?
    J'aurais utilise un stream personnellement (avec comme destination le tableau d'octet) et coder le 'Read et le 'Write pour le type a discriminant.

    M'enfin, sans savoir a quoi ca sert et un exemple compilable qui souligne ton probleme, je ne fais qu'essayer de deviner (d'ailleurs, ca devient assez general je trouve les bouts de code non compilable dans cette section. :-?).

  3. #3
    Candidat au Club
    Inscrit en
    Mai 2007
    Messages
    6
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Merci jc-miranda pour ces précisions,

    effectivement j'avais oublié la clause de représentation, ce qui aurait eu de fâcheuses conséquences.

    Par contre je ne comprends pas ce que tu appelles "un exemple compilable", le mien compile et s'exécute parfaitement sous GPS (après j'ai peut-être raté mon copier-coller, je ne dis pas).

    Pour le Stream j'y avais pensé mais je ne sais pas trop si je peux utiliser toutes les subtilités (dans l'embarqué je n'ai pas droit à grand chose )

    Pourrais-tu préciser ce point-là, je n'ai utilisé le Stream que sur des machines natives et je n'ai jamais surchargé les 'Read et 'Write. Il faut faire quoi exactement ?


    Sinon pour des précisions je vais essayer d'en apporter :
    - j'ai une structure de type requête (celle de l'exemple sous GPS n'est pas la vraie, elle est énorme alors j'ai essayé de simplifier.. et du coup oublié la clause de représentation )
    - Ensuite je dois envoyer cette structure sur un bus en utilisant une méthode de l'OS propriétaire qui donne à peu près :
    Port.Send(Pointeur_Sur_Tableau_d_Octets)

    Donc je veux que ma structure soit effectivement envoyée dans l'ordre mais il faut en plus que je passe à l'OS un pointeur sur des Octets, et donc transformer son type. Là pour le coup je me suis peut-être engagé dans la mauvaise direction.

    Je vais recopier ce que j'avais posté sur le forum et le recompiler voir si ça marche toujours.

    Merci encore !

  4. #4
    Membre habitué
    Inscrit en
    Décembre 2004
    Messages
    119
    Détails du profil
    Informations forums :
    Inscription : Décembre 2004
    Messages : 119
    Points : 156
    Points
    156
    Par défaut
    S'il s'agit d'une unique structure, tu peux peut-etre envisage un Unchecked_Conversion tout simplement?

    Vu ta structure et la taille de ton tableau (plus de 800 octets), je pensais que tu construisais un message avec de multiples structures, et je ne comprenais pas cette histoire de multiples pointeurs. ^^

    Si tu veux faire du mapping d'adresse "à la C", voila un exemple bourrin (que je trouve perso tres degueu, mais bon ^^) d'utilisation du 'Address pour remplir le buffer.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
     
    with Ada.Streams.Stream_Io; use Ada.Streams.Stream_Io;
    with Text_Io;
     
    -- Remplir un buffer a la sauvage.
    -- C'est brut de fonderie, il faut y aller sur des oeufs,
    -- Ca ne gere absolument pas les endians, donc gaffe.
    -- C'est très crade, mieux vaut implementer des Stream (ma solution préférée), mais c'est plus ... direct.
    -- Par contre, le buffer meriterait un package pour lui tout seul avec ses petites fonctions
    -- pour rendre le biniou plus propre.
    procedure Ugly is
     
       -- definition des types utilises dans l'exemple.
       type Index_T is range 1 .. 100;
     
       type Octet_T is mod 2**8;
       for Octet_T'Size use 8;
     
       type A is mod 2**16;
       for A'Size use 16;
     
       type B is mod 2**32;
       for B'Size use 32;
     
       type Buffer_T is array(Index_T range <>) of Octet_T;
     
       type R1 is
          record
             R1_1 : A;
             R1_2 : A;
          end record;
       for R1 use
          record
             R1_1 at 0 range  0 .. 15;
             R1_2 at 2 range  0 .. 15;
          end record;
       for R1'Size use 32;
     
       type R2 is
         record
            R2_1 : A;
            R2_2 : B;
         end record;
       for R2 use
          record
             R2_1 at 0 range  0 .. 15;
             R2_2 at 2 range  0 .. 31;
          end record;
       for R2'Size use 48;
     
       type Desc_T is (First, Last);
       for Desc_T use (First => 0, Last => 1);
       for Desc_T'Size use 8;
     
       -- notre type variant.
       type V(Desc : Desc_T := First) is
          record
             case Desc is
                when First =>
                   V_1 : R1;
                when Last  =>
                   V_2 : R2;
             end case;
          end record;
       for V use
          record
             Desc at 0 range  0 ..  7;
             V_1  at 1 range  0 .. 31;
             V_2  at 1 range  0 .. 47;
          end record;
     
       Buffer      : Buffer_T(1 .. 12);
       Read_Index  : Index_T := 1; -- index a gerer pour savoir ou lire
       Write_Index : Index_T := 1; -- index a gerer pour savoir ou ecrire.
     
       V_First_Size : constant Index_T := 5;
       V_Last_Size  : constant Index_T := 7;
     
       File   : File_Type;
       Output : Stream_Access;
    begin
       Create(File => File, Name => "toto.dat");
       Output := Stream(File);
     
       declare
          X : V := (First, (2,3));
          for X'Address use Buffer(Write_Index)'Address;
          -- on se positionne dans le buffer.
       begin
          Write_Index := Write_Index + V_First_Size;
          -- et on oublie pas de mettre à jour
          -- la position de la prochaine ecriture, sinon, c'est la cata!
       end;
     
       declare
          Y : V := (Last, (4,5));
          for Y'Address use Buffer(Write_Index)'Address;
       begin
          Write_Index := Write_Index + V_Last_Size;
       end;
     
       for Index in Buffer'Range loop
          Octet_T'Write(Output, Buffer(Index));
       end loop;
     
       Close(File);
     
       declare
          X : V;
          pragma Import(Ada,X); -- cet import sert a eviter qu'Ada rale a cause de l'initialisation du variant.
          for X'Address use Buffer(Read_Index)'Address;
     
       begin
          Text_Io.Put_Line(Desc_T'Image(X.Desc)  & " " &
                             A'Image(X.V_1.R1_1) & " " &
                             A'Image(X.V_1.R1_2));
          Read_Index := Read_Index + V_First_Size;
       end;
     
       declare
          X : V;
          pragma Import(Ada,X); -- comme le precedent
          for X'Address use Buffer(Read_Index)'Address;
     
       begin
          Text_Io.Put_Line(Desc_T'Image(X.Desc)  & " " &
                             A'Image(X.V_2.R2_1) & " " &
                             B'Image(X.V_2.R2_2));
       end;
     
    end Ugly;

    Le buffer est lié a la representation machine dans mon exemple, donc ca ne passera pas si tu dois respecter le network byte order et que ton proc n'est pas big endian;

    Maintenant, c'est toi qui voit.
    A+

    Juan

  5. #5
    Candidat au Club
    Inscrit en
    Mai 2007
    Messages
    6
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Citation Envoyé par jc-miranda
    S'il s'agit d'une unique structure, tu peux peut-etre envisage un Unchecked_Conversion tout simplement?

    Vu ta structure et la taille de ton tableau (plus de 800 octets), je pensais que tu construisais un message avec de multiples structures, et je ne comprenais pas cette histoire de multiples pointeurs. ^^

    Si tu veux faire du mapping d'adresse "à la C", voila un exemple bourrin (que je trouve perso tres degueu, mais bon ^^) d'utilisation du 'Address pour remplir le buffer.

    [...]

    Le buffer est lié a la representation machine dans mon exemple, donc ca ne passera pas si tu dois respecter le network byte order et que ton proc n'est pas big endian;

    Maintenant, c'est toi qui voit.
    A+

    Juan
    Merci Juan pour cet exemple c'est (presque) exactement ce que je cherchais, même si c'est "crade" tu as raison et pas portable (maudits indiens !).

    Du coup je vais essayer de passer par des unchecked_conversion, ça m'a l'air plus élégant.

    Du coup une dernière question se pose à moi : Mon type variant est pas mal mais je voudrais enlever le discriminant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
      type V(Desc : Desc_T := Last) is
          record
             case Desc is
                when First =>
                   V_1 : R1;
                when Last  =>
                   V_2 : R2;
             end case;
          end record;
       for V use
          record
             Desc at 0 range  0 ..  7;
             V_1  at 1 range  0 .. 31;
             V_2  at 1 range  0 .. 47;
          end record;
    Ce champ Desc, n'y a-t-il pas moyen de l'enlever mais en gardant une généricité ? Parce que je voudrais avoir une structure générique du style

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    type T_V_Index is (V_1, V_2, V_3, V_4);
    type Array_Of_V is array (T_V_Index) of V;
     
    type Gene is 
        record
            En_Tete : Type_En_Tete; -- je ne détaille pas ce type, on dira qu'il est simple
            Field_1 : Array_Of_V;
        end record;
     
    for Gene use 
        record
            En_Tete at 0 range 0 .. 31;
            Field_1   at 4 range 0 .. ????;  -- je ne sais pas du coup
        end record;
    Voila mon soucis à présent c'est que je ne connais pas la taille de mon tableau puisqu'il y a un variant (c'est le premier élément). En plus le Discriminant me pose des soucis car je vais devoir l'envoyer et ce n'est pas quelque chose que je veux (il n'est pas dans ma trame).

    Mais je voulais que ça reste "élégant" (avoir une trame faite d'en_tete puis d'éléments). C'est possible à votre avis ?

    Désolé d'abuser de votre patience et merci pour tous ces conseils !

    Jérôme

  6. #6
    Candidat au Club
    Inscrit en
    Mai 2007
    Messages
    6
    Détails du profil
    Informations forums :
    Inscription : Mai 2007
    Messages : 6
    Points : 4
    Points
    4
    Par défaut
    Laissez tomber j'ai finalement opté pour ne pas utiliser de tableaux avec discriminant, ce sera plus simple.

    Merci en tout cas pour le support !

    PS : Je vais clore ce topic.

    Jérôme

+ Répondre à la discussion
Cette discussion est résolue.

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo