Pregunta

Me gustaría declarar un registro en Delphi que contenga el mismo diseño que en C.

Para aquellos interesados: este registro es parte de una unión en el registro LDT_ENTRY del sistema operativo Windows. (Necesito usar este registro en Delphi porque estoy trabajando en un emulador de Xbox en Delphi; vea el proyecto Dxbx en sourceforge).

De todos modos, el registro en cuestión se define 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;

Hasta donde yo sé, no hay campos de bits posibles en Delphi. Intenté esto:

 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;

Pero, por desgracia: su tamaño se convierte en 10 bytes, en lugar de los 4 esperados. Me gustaría saber cómo debo declarar el registro, para obtener un registro con el mismo diseño, el mismo tamaño y los mismos miembros. Preferiblemente sin cargas de getter / setters.

TIA.

¿Fue útil?

Solución

¡Gracias a todos!

Basado en esta información, reduje esto 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;

El índice se codifica de la siguiente manera: (BitOffset shl 8) + NrBits. Donde 1 & Lt; = NrBits & Lt; = 32 y 0 & Lt; = BitOffset & Lt; = 31

Ahora, puedo obtener y configurar estos bits de la siguiente manera:

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

Bastante ingenioso, ¿no crees?!?!

PD: Rudy Velthuis ahora incluyó una versión revisada de esto en su excelente " Errores de convertir " -artículo .

Otros consejos

Rudy's Delphi Corner es el mejor recurso que conozco sobre la interoperabilidad de Delphi y C / C ++. Su Errores de conversión es una lectura obligada cuando se usan API C / C ++ en Delphi. El capítulo que más le interesará es Registros y alineación - & Gt; Bitfields , pero le insto a que lea todo de arriba abajo, dos veces . Los otros artículos definitivamente también merecen la inversión de tiempo.

Ok, mi manipulación de bits está un poco oxidada, por lo que podría haber invertido los bytes. Pero el siguiente código da la idea general:

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;

Bueno, básicamente necesitas llegar a lo sucio con manipulación de bits.

¿Por qué, específicamente, necesita conservar esa estructura?

Si solo necesita hablar con un programa heredado que habla en este dialecto (TCP / IP o similar), o almacena datos de esta manera (archivos, etc.), entonces mapearía una estructura Delphi normal a un versión bit compatible. En otras palabras, usaría una estructura Delphi normalmente estructurada en la memoria y escribiría código para escribir y leer esa estructura de manera compatible.

Si necesita ahorrar memoria, crearía getters y setters que manipulan bits de enteros internos o similares. Esto tendrá un impacto en el rendimiento, pero no mucho más de lo que tendría el programa C original, la única diferencia es que la manipulación de bits se agregaría mediante la magia del compilador en la versión C, mientras que tendrá que escribirla usted mismo.

Si no tiene muchos registros en la memoria y no necesita hablar con otro programa, usaría una estructura Delphi natural. La compensación por un mayor rendimiento será más memoria utilizada.

Pero todo depende de sus criterios.

En cualquier caso, no podrá convencer al compilador de Delphi para que haga el mismo trabajo que el compilador de C.

REGISTRO ENVASADO, sugerido por otro aquí, no hace eso, y nunca fue hecho. Solo eliminará el relleno de alineación para colocar enteros en límites de 32 bits y similares, pero no empaquetará múltiples campos en un byte.

Tenga en cuenta que una forma común de hacerlo es a través de Delphi SETS, que se implementan internamente utilizando campos de bits. Nuevamente, tendrá un código diferente al de la variante C.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top