Question

J'aimerais déclarer un enregistrement dans Delphi qui contient la même présentation que dans C.

Pour les personnes intéressées: cet enregistrement fait partie d'une union de l'enregistrement LDT_ENTRY du système d'exploitation Windows. (Je dois utiliser cet enregistrement dans Delphi car je travaille sur un émulateur Xbox dans Delphi - voir le projet Dxbx sur sourceforge).

Quoi qu’il en soit, l’enregistrement en question est défini comme suit:

    struct
    {
        DWORD   BaseMid : 8;
        DWORD   Type : 5;
        DWORD   Dpl : 2;
        DWORD   Pres : 1;
        DWORD   LimitHi : 4;
        DWORD   Sys : 1;
        DWORD   Reserved_0 : 1;
        DWORD   Default_Big : 1;
        DWORD   Granularity : 1;
        DWORD   BaseHi : 8;
    }
    Bits;

Pour autant que je sache, il n’ya pas de champs de bits possibles dans Delphi. J'ai essayé ceci:

 Bits = record
      BaseMid: Byte; // 8 bits
      _Type: 0..31; // 5 bits
      Dpl: 0..3; // 2 bits
      Pres: Boolean; // 1 bit
      LimitHi: 0..15; // 4 bits
      Sys: Boolean; // 1 bit
      Reserved_0: Boolean; // 1 bit
      Default_Big: Boolean; // 1 bit
      Granularity: Boolean; // 1 bit
      BaseHi: Byte; // 8 bits
  end;

Mais hélas: sa taille devient 10 octets au lieu des 4 prévus. Je voudrais savoir comment déclarer le disque, afin d’obtenir un disque avec la même mise en page, la même taille et les mêmes membres. De préférence sans charges de getter / setters.

TIA.

Était-ce utile?

La solution

Merci à tous!

Sur la base de ces informations, j'ai réduit ceci à:

RBits = record
public
  BaseMid: BYTE;
private
  Flags: WORD;
  function GetBits(const aIndex: Integer): Integer;
  procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
  BaseHi: BYTE;
  property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
  property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
  property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
  property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
  property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
  property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
  property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
  property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;

L'index est codé comme suit: (BitOffset shl 8) + NrBits. Où 1 & Lt; = NrBits & Lt; = 32 et 0 & Lt; = BitOffset & Lt; = 31

Maintenant, je peux obtenir et définir ces bits comme suit:

{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);

  Result := (Flags shr Offset) and Mask;
end;

procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
  Offset: Integer;
  NrBits: Integer;
  Mask: Integer;
begin
  NrBits := aIndex and $FF;
  Offset := aIndex shr 8;

  Mask := ((1 shl NrBits) - 1);
  Assert(aValue <= Mask);

  Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;

Assez chouette, vous ne pensez pas?!?!

PS: Rudy Velthuis en a désormais ajouté une version révisée dans son excellent & "Les pièges de la conversion de &"; article .

Autres conseils

Le coin Delphi de Rudy est la meilleure ressource que je connaisse concernant l'interopérabilité entre Delphi et C / C ++. Ses pièges de la conversion sont à lire absolument lors de l'utilisation des API C / C ++ dans Delphi. Le chapitre qui vous intéresse le plus est Enregistrements et alignement - & Gt; Bitfields , mais je vous exhorte à lire l'intégralité du texte, deux fois . Les autres articles valent également la peine d’être investis dans le temps.

Ok, ma manipulation de bits est un peu rouillée, donc j’aurais pu inverser les octets. Mais le code ci-dessous donne l’idée générale:

type
  TBits = record
  private
    FBaseMid     : Byte;
    FTypeDplPres :  Byte;
    FLimitHiSysEa: Byte;
    FBaseHi      : Byte;

    function GetType: Byte;
    procedure SetType(const AType: Byte);
    function GetDpl: Byte;
    procedure SetDbl(const ADpl: Byte);
    function GetBit1(const AIndex: Integer): Boolean;
    procedure SetBit1(const AIndex: Integer; const AValue: Boolean);
    function GetLimitHi: Byte;
    procedure SetLimitHi(const AValue: Byte);
    function GetBit2(const AIndex: Integer): Boolean;
    procedure SetBit2(const AIndex: Integer; const AValue: Boolean);

  public
    property BaseMid: Byte read FBaseMid write FBaseMid;
    property &Type: Byte read GetType write SetType; // 0..31
    property Dpl: Byte read GetDpl write SetDbl; // 0..3
    property Pres: Boolean index 128 read GetBit1 write SetBit1; 
    property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15

    property Sys: Boolean index 16 read GetBit2 write SetBit2; 
    property Reserved0: Boolean index 32 read GetBit2 write SetBit2; 
    property DefaultBig: Boolean index 64 read GetBit2 write SetBit2; 
    property Granularity: Boolean index 128 read GetBit2 write SetBit2; 
    property BaseHi: Byte read FBaseHi write FBaseHi;
  end;

  function TBits.GetType: Byte;
  begin
    Result := (FTypeDplPres shr 3) and $1F;
  end;

  procedure TBits.SetType(const AType: Byte);
  begin
    FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3);
  end;

  function TBits.GetDpl: Byte;
  begin
    Result := (FTypeDplPres and $06) shr 1;
  end;

  procedure TBits.SetDbl(const ADpl: Byte);
  begin
    FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1);
  end;

  function TBits.GetBit1(const AIndex: Integer): Boolean;
  begin
    Result := FTypeDplPres and AIndex = AIndex;
  end;

  procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FTypeDblPres := FTypeDblPres or AIndex
    else
      FTypeDblPres := FTypeDblPres and not AIndex;
  end;

  function TBits.GetLimitHi: Byte;
  begin
    Result := (FLimitHiSysEa shr 4) and $0F;
  end;

  procedure TBits.SetLimitHi(const AValue: Byte);
  begin
    FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4);
  end;

  function TBits.GetBit2(const AIndex: Integer): Boolean;
  begin
    Result := FLimitHiSysEa and AIndex = AIndex;
  end;

  procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean);
  begin
    if AValue then
      FLimitHiSysEa := FLimitHiSysEa or AIndex
    else
      FLimitHiSysEa := FLimitHiSysEa and not AIndex;
  end;

Eh bien, vous devez fondamentalement vous attaquer à la manipulation de bits.

Pourquoi avez-vous besoin de conserver cette structure?

Si vous avez seulement besoin de parler à un programme existant qui parle dans ce dialecte (TCP / IP ou similaire), ou qui stocke les données de cette manière (fichiers, etc.), je mapperais une structure Delphi normale sur une version compatible bit. En d’autres termes, j’utiliserais une structure Delphi normalement structurée en mémoire et écrirais du code pour écrire et lire cette structure de manière compatible.

Si vous avez besoin d'économiser de la mémoire, je créerais des getters et des setters manipulant des bits d'entiers internes ou similaires. Cela aura un impact sur les performances, mais pas beaucoup plus que ce que le programme C original aurait, la seule différence est que la manipulation de bits serait ajoutée par la magie du compilateur dans la version C, alors que vous devrez l'écrire vous-même.

Si vous n'avez pas beaucoup d'enregistrements en mémoire et n'avez pas besoin de parler à un autre programme, j'utiliserais une structure Delphi naturelle. Un compromis pour de meilleures performances sera une utilisation plus importante de la mémoire.

Mais tout dépend de vos critères.

Dans tous les cas, vous ne pourrez pas amener le compilateur Delphi à faire le même travail pour vous en tant que compilateur C.

PACKED RECORD, suggéré par un autre ici, ne fait pas cela et n’a jamais été conçu. Il supprimera uniquement le remplissage d'alignement pour placer des entiers sur des limites de 32 bits et similaires, mais ne regroupera pas plusieurs champs dans un octet.

Notez qu'une manière courante de procéder consiste à utiliser Delphi SETS, qui implémente en interne à l'aide de champs de bits. Encore une fois, vous aurez un code différent de celui de la variante C.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top