델파이 레코드에서 비트 필드를 시뮬레이션하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/282019

  •  08-07-2019
  •  | 
  •  

문제

델파이에서 C에서와 동일한 레이아웃을 포함하는 레코드를 선언하고 싶습니다.

관심있는 사람들을 위해 :이 레코드는 Windows OS의 LDT_ENTRY 레코드에서 노조의 일부입니다. (Delphi의 Xbox 에뮬레이터에서 작업하고 있기 때문에 Delphi 에서이 레코드를 사용해야합니다. Sourceforge의 Project 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;

그러나 아아 : 크기는 예상 4 대신 10 바이트가됩니다. 4. 레코드를 어떻게 선언 해야하는지 알고 싶습니다. 그래서 동일한 레이아웃, 동일한 크기 및 동일한 멤버로 레코드를 얻을 수 있습니다. 많은 getter/setters가없는 선호합니다.

티아.

도움이 되었습니까?

해결책

모두 감사합니다!

이 정보를 바탕으로 다음과 같이 줄였습니다.

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. 여기서 1 <= 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는 이제 그의 우수한 수정 된 버전을 포함 시켰습니다. "변환의 함정"-항.

다른 팁

루디의 델파이 코너 Delphi 및 C/C ++ 상호 운용성에 대해 내가 아는 최고의 리소스입니다. 그의 전환의 함정 Delphi에서 C/C ++ API를 사용할 때는 거의 읽어야합니다. 가장 관심이있는 장은 IS입니다 기록 및 정렬 -> 비트 필드,하지만 나는 당신이 모든 것을 위에서 아래로 읽을 것을 촉구합니다. 두 배. 다른 기사도 시간 투자 가치도 있습니다.

좋아, 내 비트 조작은 약간 녹슬으므로 바이트를 되돌릴 수있었습니다. 그러나 아래 코드는 일반적인 아이디어를 제공합니다.

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;

글쎄, 당신은 기본적으로 비트 조작으로 더러운 곳으로 내려 가야합니다.

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

이 방언 (TCP/IP 또는 이와 유사한) 또는 이러한 방식으로 데이터를 저장하는 레거시 프로그램에 대해서만 대화 해야하는 경우 (파일 등) 일반 델파이 구조를 비트 버전에 매핑합니다. 호환 가능. 다시 말해, 나는 메모리에 정상적으로 구조화 된 델파이 구조를 사용하고 코드를 작성하여 그 구조를 호환 가능한 방식으로 작성하고 읽습니다.

메모리를 저장 해야하는 경우 내부 정수 또는 이와 유사한 비트를 조작하는 게터와 세터를 만들 것입니다. 이것은 성능 영향을 미치지 만 원래 C 프로그램의 것보다 훨씬 많지는 않지만 유일한 차이점은 C 버전의 컴파일러 매직에 의해 비트 관리가 추가되지만 직접 작성해야한다는 것입니다.

메모리에 기록이 많지 않고 다른 프로그램과 대화 할 필요가 없다면 자연스러운 델파이 구조를 사용하겠습니다. 더 높은 성능을위한 트레이드 오프는 더 많은 메모리를 사용합니다.

그러나 그것은 모두 당신의 기준에 달려 있습니다.

어쨌든 델파이 컴파일러와 동일한 작업을 수행하여 C 컴파일러와 동일한 작업을 수행 할 수 없습니다.

여기 다른 사람이 제안한 포장 된 기록은 그렇게하지 않으며 결코 의도하지 않았습니다. 정렬 패딩 만 제거하여 32 비트 경계와 유사한 정수를 넣지 만 여러 필드를 하나의 바이트로 포장하지는 않습니다.

이를 수행하는 일반적인 방법은 비트 필드를 사용하여 내부적으로 구현하는 델파이 세트를 통한 것입니다. 다시, C 변형과 다른 코드가 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top