Pergunta

Gostaria de declarar um registro em Delphi que contém o mesmo layout como tem em C.

Para os interessados: Este registro é parte de uma união no registro LDT_ENTRY do sistema operacional Windows a. (Eu preciso usar este registro em Delphi, porque eu estou trabalhando em um emulador de Xbox em Delphi - veja projecto Dxbx no SourceForge)

.

De qualquer forma, o registro em questão é definido como:

    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;

Tanto quanto eu sei, não há campos de bit possíveis em Delphi. Eu tentei isso:

 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;

Mas, infelizmente: o seu tamanho torna-se 10 bytes, em vez dos 4 esperado. Eu gostaria de saber como devo declarar o registro, para que eu recebo um registro com o mesmo layout, o mesmo tamanho, e os mesmos membros. De preferência, sem cargas de getter / setters.

TIA.

Foi útil?

Solução

Obrigado a todos!

Com base nesta informação, eu reduzi isso:

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;

O índice é codificado como segue: (BitOffset shl 8) + NrBits. Onde 1 <= NrBits <= 32 e 0 <= BitOffset <= 31

Agora, eu posso obter e definir esses bits da seguinte forma:

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

bacana bonita, você não acha?!?!

PS: Rudy Velthuis agora incluída uma versão revisada deste em seu excelente "Armadilhas de converter" artigo .

Outras dicas

de Rudy Delphi Canto é o melhor recurso que eu conheço sobre Delphi e C / C ++ interoperabilidade. Sua Armadilhas da conversão é praticamente uma leitura obrigatória quando se utiliza APIs C / C ++ em Delphi. O capítulo você estará em mais interessado é Registros e alinhamento -> Campos binários , mas peço-lhe para ler toda a parte superior coisa para baixo, duas vezes . Os outros artigos são definitivamente vale a pena o investimento de tempo, também.

Ok, meu pouco manipulação é um pouco enferrujado, então eu poderia ter revertido os bytes. Mas o código abaixo dá a idéia geral:

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;

Bem, basicamente você precisa para chegar até o sujo com bit-manipulation.

Por que, especificamente, você precisa manter essa estrutura?

Se você só precisa falar com um programa legado que tanto fala neste dialeto (TCP / IP ou semelhantes), ou armazenamentos de dados dessa maneira (arquivos, etc.), então gostaria de mapear uma estrutura normal Delphi a um bit-versão compatível. Em outras palavras, gostaria de usar uma estrutura de Delphi normalmente estruturado na memória e escrever código para escrever e ler que a estrutura de uma maneira compatível.

Se você precisar economizar memória, eu faria getters e setters que manipulam pedaços de inteiros internos ou similar. Isto terá um impacto no desempenho, mas não muito mais do que aquilo que o programa C original teria, a única diferença é que o bit-manipulation seria adicionado pela magia do compilador na versão C, ao passo que você terá que escrevê-lo sozinho.

Se você não tem muitos registros na memória, e não precisa falar com outro programa, eu usaria uma estrutura Delphi natural. Trade-off para maior desempenho será mais memória utilizado.

Mas tudo depende de seus critérios.

Em qualquer caso, você não será capaz de falar o compilador Delphi para fazer o mesmo trabalho para você como o compilador C.

RECORDE embaladas, sugerido por outro aqui, não fazer isso, e nunca foi concebido para. Ele só irá remover preenchimento de alinhamento para colocar inteiros nos limites de 32 bits e semelhante, mas não irá embalar vários campos em um byte.

Note que uma maneira comum de fazer isso é através de conjuntos de Delphi, que estão a implementar internamente usando bit-campos. Mais uma vez, você terá código diferente do que a variante C.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top