2008-11-11 5 views
24

델파이에서 C와 같은 레이아웃을 포함하는 레코드를 선언하고 싶습니다.Delphi 레코드에서 비트 필드를 시뮬레이트하는 방법은 무엇입니까?

관심있는 사람들을위한 :이 레코드는 Windows OS의 LDT_ENTRY 레코드에있는 유니온의 일부입니다. (필자는 Delphi에서 Xbox 에뮬레이터로 작업 중이므로이 레코드를 Delphi에서 사용해야합니다 - sourceforge의 Dxbx 프로젝트 참조).

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; 

는 지금까지 내가 아는 한, 델파이 수없는 비트 필드가없는 :로

어쨌든, 문제의 레코드가 정의된다.

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; 

을하지만 슬프게도 : 나는이 시도 않았다 그것의 크기는 10 바이트가된다, 대신 내가하고 싶은 예상 4. 의 내가 기록을 선언하는 방법을 알고, 내가 같은 레이아웃 기록을 얻을 수 있도록 , 같은 크기, 그리고 같은 회원. 가급적 getter/setter가 없어도 좋습니다.

TIA.

답변

28

감사합니다. 다음 인덱스가 부호화된다

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; 

: (BitOffset shl 8) + NrBits이 정보를 기반

난이 감소. 어디 한 < = NrBits < = 32 지금 0 < = BitOffset < = 31

, 내가받을 다음과 같이 이러한 비트를 설정할 수 있습니다

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

는 꽤 멋진, 당신은 생각하지 않아?!? !

추 신 : Rudy Velthuis가 그의 "Pitfalls of converting"-article에 수정 된 버전을 포함 시켰습니다.

+0

정말 좋은 생각입니다. – gabr

+0

칭찬에 감사드립니다. 나는 코드에서 몇 가지 실수를 저 지르지 만, 지금은 해결했습니다. 건배! – PatrickvL

+0

감사합니다. 매우 유용합니다. '플래그'는 정수 유형이 아니어야합니다. – JustMe

0

음, 기본적으로 비트 조작으로 더러워 져야합니다.

왜 구체적으로 그 구조를 유지해야합니까?

이 방언 (TCP/IP 또는 유사)에서 대화하거나 이러한 방식으로 데이터를 저장하는 기존 프로그램 (파일 등) 만 얘기하면 정상적인 델파이 구조를 다음과 같이 매핑합니다. 비트 버전 호환. 즉, 일반적으로 구조화 된 Delphi 구조체를 메모리에 사용하고 해당 구조를 호환 방식으로 작성하고 읽는 코드를 작성합니다.

메모리를 절약해야하는 경우 getters 및 setter를 사용하여 내부 정수 또는 이와 유사한 비트를 조작합니다. 이것은 성능에 영향을 미치지 만 원래의 C 프로그램보다 훨씬 더 많은 차이점은 컴파일러가 C 버전에서 비트 조작을 추가한다는 것입니다. 반면에 직접 작성해야합니다.

메모리에 레코드가 많지 않고 다른 프로그램과 대화 할 필요가없는 경우 자연스러운 Delphi 구조체를 사용합니다. 더 높은 성능에 대한 절충은 더 많은 메모리 사용이 될 것입니다.

하지만 모두 귀하의 기준에 따라 다릅니다.

어쨌든 델파이 컴파일러는 C 컴파일러와 똑같은 작업을 수행 할 수 없습니다.

여기에 다른 사람이 제안한 포장 된 레코드는 그렇게하지 않았으며 결코 의미가 없었습니다. 정렬 패딩을 제거하여 정수를 32 비트 경계 및 유사하게 배치하지만 여러 필드를 한 바이트로 묶지는 않습니다.

이 작업을 수행하는 일반적인 방법은 내부적으로 비트 필드를 사용하여 구현되는 Delphi SETS를 사용하는 것입니다. 다시 말하지만 C 변형과 다른 코드를 사용하게됩니다.

14

Rudy's Delphi Corner은 Delphi 및 C/C++ 상호 운용성과 관련하여 내가 아는 최고의 자료입니다. 그의 Pitfalls of conversion은 Delphi에서 C/C++ API를 사용할 때 꽤 많이 읽혀 져야합니다. 가장 관심이있는 부분은 Records and alignment -> Bitfields이지만, 전체 내용을 위에서 아래로 읽어 보려면 을 두 번 두 번 읽으시기 바랍니다. 다른 기사도 시간 투자 가치가 있습니다.

5

좋아, 내 비트 조작 약간 녹슬었고, 그래서 바이트를 반전시킬 수 있었다. 아래의 코드는 일반적인 아이디어입니다.

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; 
관련 문제