Domanda

Vorrei dichiarare un record in Delphi che contenga lo stesso layout di C.

Per chi fosse interessato:Questo record fa parte di un'unione nel record LDT_ENTRY del sistema operativo Windows.(Ho bisogno di usare questo record in Delphi perché sto lavorando su un emulatore Xbox in Delphi - vedi progetto Dxbx su sourceforge).

In ogni caso il record in questione è definito come:

    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;

Per quanto ne so, in Delphi non sono possibili bitfield.Ho provato questo:

 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;

Ma ahimè:la sua dimensione diventa 10 byte, invece dei 4 previsti.Vorrei sapere come devo dichiarare il record, in modo da ottenere un record con lo stesso layout, la stessa dimensione e gli stessi membri.Preferibilmente senza carichi getter/setter.

TIA.

È stato utile?

Soluzione

Grazie a tutti!

Sulla base di queste informazioni, ho ridotto questo a:

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'indice è codificato come segue: (BitOffset shl 8) + NrBits.Dove 1<=NrBits<=32 e 0<=BitOffset<=31

Ora posso ottenere e impostare questi bit come segue:

{$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;

Abbastanza carino, non credi?!?!

PS:Rudy Velthuis ne ha ora incluso una versione rivista nel suo eccellente Articolo "Le insidie ​​​​della conversione"..

Altri suggerimenti

L'angolo Delfi di Rudy è la migliore risorsa che conosco per quanto riguarda l'interoperabilità di Delphi e C/C++.Il suo Insidie ​​della conversione è praticamente una lettura obbligata quando si utilizzano le API C/C++ in Delphi.Il capitolo che ti interesserà di più è Record e allineamento -> Bitfield, ma ti invito a leggere tutto da cima a fondo, due volte.Anche gli altri articoli valgono sicuramente l’investimento di tempo.

Ok, la mia manipolazione dei bit è un po 'arrugginita, quindi avrei potuto invertire i byte. Ma il codice seguente fornisce l'idea generale:

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;

Bene, in pratica devi andare fino in fondo con la manipolazione dei bit.

Perché, in particolare, è necessario conservare quella struttura?

Se hai solo bisogno di parlare con un programma legacy che parla in questo dialetto (TCP / IP o simile), o memorizza i dati in questo modo (file, ecc.), allora mapperei una normale struttura Delphi a un versione bit compatibile. In altre parole, vorrei utilizzare una struttura Delphi normalmente strutturata in memoria e scrivere codice per scrivere e leggere quella struttura in modo compatibile.

Se hai bisogno di risparmiare memoria, creerei getter e setter che manipolano bit di interi interni o simili. Ciò avrà un impatto sulle prestazioni, ma non molto più di quello che avrebbe il programma C originale, l'unica differenza è che la manipolazione dei bit sarebbe aggiunta dalla magia del compilatore nella versione C, mentre dovrai scriverla tu stesso.

Se non hai molti dischi in memoria e non hai bisogno di parlare con un altro programma, userei una struttura Delphi naturale. Il compromesso per prestazioni più elevate verrà utilizzato più memoria.

Ma tutto dipende dai tuoi criteri.

In ogni caso, non sarai in grado di convincere il compilatore Delphi a fare lo stesso lavoro per te del compilatore C.

RECORD CONFEZIONATO, suggerito da un altro qui, non lo fa e non è mai stato pensato. Rimuoverà solo il riempimento di allineamento per mettere numeri interi su limiti a 32 bit e simili, ma non impacchetterà più campi in un byte.

Si noti che un modo comune per farlo è tramite Delphi SETS, che stanno implementando internamente usando i bit-field. Ancora una volta, avrai un codice diverso rispetto alla variante C.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top