2008-11-11 9 views
24

Ich möchte einen Datensatz in Delphi deklarieren, die das gleiche Layout enthält, wie es in C. hatWie simulieren Bit-Felder in Delphi-Datensätzen?

Für Interessenten: Dieser Datensatz Teil einer Vereinigung in der Windows-Betriebssysteme LDT_ENTRY Rekord. (Ich muss diesen Eintrag in Delphi verwenden, da ich in Delphi an einem Xbox-Emulator arbeite - siehe Projekt Dxbx auf sourceforge).

Wie dem auch sei, wird der Datensatz in Frage wie folgt definiert:

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; 

Soweit ich weiß, gibt es keine Bit-Felder möglich in Delphi sind. Ich versuche dies:

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; 

Aber ach: seine Größe wird 10 Bytes, statt dem erwarteten 4. Ich mag würde zu wissen, wie ich die Platte erklären sollte, so dass ich einen Datensatz mit dem gleichen Layout erhalten , die gleiche Größe und die gleichen Mitglieder. Vorzugsweise ohne viele Getter/Setter.

TIA.

Antwort

28

Danke allen!

basierend auf dieser Information, I reduziert dies zu:

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; 

Der Index wie folgt codiert wird: (BitOffset shl 8) + NrBits. Wo 1 < = NrBits < = 32 und 0 < = BitOffset < = 31

Jetzt kann ich diese Bits erhalten und wie folgt festgelegt:

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

Ziemlich raffiniert, meinst du nicht?!? !

PS: Rudy Velthuis jetzt eine überarbeitete Version von diesem in seiner ausgezeichneten "Pitfalls of converting"-article enthalten.

+0

Das ist eine wirklich gute Idee! – gabr

+0

Danke für das Kompliment. Ich habe ein paar Fehler im Code gemacht, die ich jetzt behoben habe, Prost! – PatrickvL

+0

Danke, das ist sehr hilfreich. "Flags" sollte kein Integer-Typ sein? – JustMe

0

Nun, Sie müssen im Grunde genommen mit Bit-Manipulation in den Schmutzigen gehen.

Warum, speziell, müssen Sie diese Struktur beibehalten?

Wenn Sie nur mit einem Legacy-Programm sprechen müssen, das entweder in diesem Dialekt (TCP/IP oder ähnlich) spricht, oder Daten auf diese Weise speichert (Dateien usw.), dann würde ich eine normale Delphi-Struktur zuordnen eine Bit-Version kompatibel. Mit anderen Worten, ich würde eine normal strukturierte Delphi-Struktur im Speicher verwenden und Code schreiben, um diese Struktur in einer kompatiblen Weise zu schreiben und zu lesen.

Wenn Sie Speicher sparen müssen, würde ich Getter und Setter machen, die Bits von internen Ganzzahlen oder ähnliches manipulieren. Dies hat zwar Auswirkungen auf die Performance, aber nicht viel mehr als das, was das ursprüngliche C-Programm hätte. Der einzige Unterschied ist, dass die Bit-Manipulation durch Compiler-Magie in der C-Version hinzugefügt wird, während Sie sie selbst schreiben müssen.

Wenn Sie nicht viele Datensätze im Speicher haben und nicht mit einem anderen Programm sprechen müssen, würde ich eine natürliche Delphi-Struktur verwenden. Kompromiss für höhere Leistung wird mehr Speicher verwendet werden.

Aber alles hängt von Ihren Kriterien ab.

In jedem Fall können Sie den Delphi-Compiler nicht dazu bringen, den gleichen Job für Sie wie der C-Compiler zu machen.

PACKED RECORD, von einem anderen hier vorgeschlagen, tut das nicht, und war nie dazu gedacht. Es wird nur die Ausrichtungspadding entfernen, um Ganzzahlen auf 32-Bit-Grenzen und ähnliches zu setzen, aber wird nicht mehrere Felder in ein Byte packen.

Beachten Sie, dass eine gängige Methode Delphi-SETS ist, die intern Bit-Felder implementieren. Auch hier haben Sie einen anderen Code als die C-Variante.

14

Rudy's Delphi Corner ist die beste Ressource, die ich über Delphi und C/C++ - Interoperabilität weiß. Sein Pitfalls of conversion ist so ziemlich ein Muss bei der Verwendung von C/C++ - APIs in Delphi. Das Kapitel, das Sie am meisten interessiert, ist Records and alignment -> Bitfields, aber ich fordere Sie auf, das gesamte Ding von oben nach unten zu lesen, zweimal. Auch die anderen Artikel sind die Investition wert.

5

Ok, meine Bitmanipulation ist ein bisschen rostig, also könnte ich die Bytes umgekehrt haben. Aber der Code unten gibt die allgemeine Idee:

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;